001/* 002 * $RCSfile: J2KImageReader.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.7 $ 042 * $Date: 2006/10/05 01:08:30 $ 043 * $State: Exp $ 044 */ 045package com.sun.media.imageioimpl.plugins.jpeg2000; 046 047import java.awt.Rectangle; 048import java.awt.Point; 049 050import javax.imageio.IIOException; 051import javax.imageio.ImageReader; 052import javax.imageio.ImageReadParam; 053import javax.imageio.ImageTypeSpecifier; 054import javax.imageio.metadata.IIOMetadata; 055import javax.imageio.spi.ImageReaderSpi; 056import javax.imageio.stream.ImageInputStream; 057import com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam; 058 059import java.awt.image.BufferedImage; 060import java.awt.image.Raster; 061import java.awt.image.RenderedImage; 062 063import java.io.*; 064import java.util.List; 065import java.util.Iterator; 066import java.util.ArrayList; 067 068import jj2000.j2k.quantization.dequantizer.*; 069import jj2000.j2k.wavelet.synthesis.*; 070import jj2000.j2k.image.invcomptransf.*; 071import jj2000.j2k.fileformat.reader.*; 072import jj2000.j2k.codestream.reader.*; 073import jj2000.j2k.entropy.decoder.*; 074import jj2000.j2k.codestream.*; 075import jj2000.j2k.decoder.*; 076import jj2000.j2k.image.*; 077import jj2000.j2k.util.*; 078import jj2000.j2k.roi.*; 079import jj2000.j2k.io.*; 080import jj2000.j2k.*; 081 082/** This class is the Java Image IO plugin reader for JPEG 2000 JP2 image file 083 * format. It has the capability to load the compressed bilevel images, 084 * color-indexed byte images, or multi-band images in byte/ushort/short/int 085 * data type. It may subsample the image, select bands, clip the image, 086 * and shift the decoded image origin if the proper decoding parameter 087 * are set in the provided <code>J2KImageReadParam</code>. 088 */ 089public class J2KImageReader extends ImageReader implements MsgLogger { 090 /** The input stream where reads from */ 091 private ImageInputStream iis = null; 092 093 /** Stream position when setInput() was called. */ 094 private long streamPosition0; 095 096 /** Indicates whether the header is read. */ 097 private boolean gotHeader = false; 098 099 /** The image width. */ 100 private int width; 101 102 /** The image height. */ 103 private int height; 104 105 /** Image metadata, valid for the imageMetadataIndex only. */ 106 private J2KMetadata imageMetadata = null; 107 108 /** The image index for the cached metadata. */ 109 private int imageMetadataIndex = -1; 110 111 /** The J2K HeaderDecoder defined in jj2000 packages. Used to extract image 112 * header information. 113 */ 114 private HeaderDecoder hd; 115 116 /** The J2KReadState for this reading session based on the current input 117 * and J2KImageReadParam. 118 */ 119 private J2KReadState readState = null; 120 121 /** 122 * Whether to log JJ2000 messages. 123 */ 124 private boolean logJJ2000Msg = false; 125 126 /** Wrapper for the protected method <code>computeRegions</code>. So it 127 * can be access from the classes which are not in <code>ImageReader</code> 128 * hierarchy. 129 */ 130 public static void computeRegionsWrapper(ImageReadParam param, 131 boolean allowZeroDestOffset, 132 int srcWidth, 133 int srcHeight, 134 BufferedImage image, 135 Rectangle srcRegion, 136 Rectangle destRegion) { 137 if (srcRegion == null) { 138 throw new IllegalArgumentException(I18N.getString("J2KImageReader0")); 139 } 140 if (destRegion == null) { 141 throw new IllegalArgumentException(I18N.getString("J2KImageReader1")); 142 } 143 144 // Clip that to the param region, if there is one 145 int periodX = 1; 146 int periodY = 1; 147 int gridX = 0; 148 int gridY = 0; 149 if (param != null) { 150 Rectangle paramSrcRegion = param.getSourceRegion(); 151 if (paramSrcRegion != null) { 152 srcRegion.setBounds(srcRegion.intersection(paramSrcRegion)); 153 } 154 periodX = param.getSourceXSubsampling(); 155 periodY = param.getSourceYSubsampling(); 156 gridX = param.getSubsamplingXOffset(); 157 gridY = param.getSubsamplingYOffset(); 158 srcRegion.translate(gridX, gridY); 159 srcRegion.width -= gridX; 160 srcRegion.height -= gridY; 161 if(allowZeroDestOffset) { 162 destRegion.setLocation(param.getDestinationOffset()); 163 } else { 164 Point destOffset = param.getDestinationOffset(); 165 if(destOffset.x != 0 || destOffset.y != 0) { 166 destRegion.setLocation(param.getDestinationOffset()); 167 } 168 } 169 } 170 171 // Now clip any negative destination offsets, i.e. clip 172 // to the top and left of the destination image 173 if (destRegion.x < 0) { 174 int delta = -destRegion.x*periodX; 175 srcRegion.x += delta; 176 srcRegion.width -= delta; 177 destRegion.x = 0; 178 } 179 if (destRegion.y < 0) { 180 int delta = -destRegion.y*periodY; 181 srcRegion.y += delta; 182 srcRegion.height -= delta; 183 destRegion.y = 0; 184 } 185 186 // Now clip the destination Region to the subsampled width and height 187 int subsampledWidth = (srcRegion.width + periodX - 1)/periodX; 188 int subsampledHeight = (srcRegion.height + periodY - 1)/periodY; 189 destRegion.width = subsampledWidth; 190 destRegion.height = subsampledHeight; 191 192 // Now clip that to right and bottom of the destination image, 193 // if there is one, taking subsampling into account 194 if (image != null) { 195 Rectangle destImageRect = new Rectangle(0, 0, 196 image.getWidth(), 197 image.getHeight()); 198 destRegion.setBounds(destRegion.intersection(destImageRect)); 199 if (destRegion.isEmpty()) { 200 throw new IllegalArgumentException 201 (I18N.getString("J2KImageReader2")); 202 } 203 204 int deltaX = destRegion.x + subsampledWidth - image.getWidth(); 205 if (deltaX > 0) { 206 srcRegion.width -= deltaX*periodX; 207 } 208 int deltaY = destRegion.y + subsampledHeight - image.getHeight(); 209 if (deltaY > 0) { 210 srcRegion.height -= deltaY*periodY; 211 } 212 } 213 if (srcRegion.isEmpty() || destRegion.isEmpty()) { 214 throw new IllegalArgumentException(I18N.getString("J2KImageReader3")); 215 } 216 } 217 218 /** Wrapper for the protected method <code>checkReadParamBandSettings</code>. 219 * So it can be access from the classes which are not in 220 * <code>ImageReader</code> hierarchy. 221 */ 222 public static void checkReadParamBandSettingsWrapper(ImageReadParam param, 223 int numSrcBands, 224 int numDstBands) { 225 checkReadParamBandSettings(param, numSrcBands, numDstBands); 226 } 227 228 /** 229 * Convert a rectangle provided in the coordinate system of the JPEG2000 230 * reference grid to coordinates at a lower resolution level where zero 231 * denotes the lowest resolution level. 232 * 233 * @param r A rectangle in references grid coordinates. 234 * @param maxLevel The highest resolution level in the image. 235 * @param level The resolution level of the returned rectangle. 236 * @param subX The horizontal subsampling step size. 237 * @param subY The vertical subsampling step size. 238 * @return The parameter rectangle converted to a lower resolution level. 239 * @throws IllegalArgumentException if <core>r</code> is <code>null</code>, 240 * <code>maxLevel</code> or <code>level</code> is negative, or 241 * <code>level</code> is greater than <code>maxLevel</code>. 242 */ 243 static Rectangle getReducedRect(Rectangle r, int maxLevel, int level, 244 int subX, int subY) { 245 if(r == null) { 246 throw new IllegalArgumentException("r == null!"); 247 } else if(maxLevel < 0 || level < 0) { 248 throw new IllegalArgumentException("maxLevel < 0 || level < 0!"); 249 } else if(level > maxLevel) { 250 throw new IllegalArgumentException("level > maxLevel"); 251 } 252 253 // At the highest level; return the parameter. 254 if(level == maxLevel && subX == 1 && subY == 1) { 255 return r; 256 } 257 258 // Resolution divisor is step*2^(maxLevel - level). 259 int divisor = 1 << (maxLevel - level); 260 int divX = divisor*subX; 261 int divY = divisor*subY; 262 263 // Convert upper left and lower right corners. 264 int x1 = (r.x + divX - 1)/divX; 265 int x2 = (r.x + r.width + divX - 1)/divX; 266 int y1 = (r.y + divY - 1)/divY; 267 int y2 = (r.y + r.height + divY - 1)/divY; 268 269 // Create lower resolution rectangle and return. 270 return new Rectangle(x1, y1, x2 - x1, y2 - y1); 271 } 272 273 /** Wrapper for the protected method <code>processImageUpdate</code> 274 * So it can be access from the classes which are not in 275 * <code>ImageReader</code> hierarchy. 276 */ 277 public void processImageUpdateWrapper(BufferedImage theImage, 278 int minX, int minY, 279 int width, int height, 280 int periodX, int periodY, 281 int[] bands) { 282 processImageUpdate(theImage, 283 minX, minY, 284 width, height, 285 periodX, periodY, 286 bands); 287 } 288 289 /** Wrapper for the protected method <code>processImageProgress</code> 290 * So it can be access from the classes which are not in 291 * <code>ImageReader</code> hierarchy. 292 */ 293 public void processImageProgressWrapper(float percentageDone) { 294 processImageProgress(percentageDone); 295 } 296 297 /** Constructs <code>J2KImageReader</code> from the provided 298 * <code>ImageReaderSpi</code>. 299 */ 300 public J2KImageReader(ImageReaderSpi originator) { 301 super(originator); 302 303 this.logJJ2000Msg = Boolean.getBoolean("jj2000.j2k.decoder.log"); 304 305 FacilityManager.registerMsgLogger(null, this); 306 } 307 308 /** Overrides the method defined in the superclass. */ 309 public void setInput(Object input, 310 boolean seekForwardOnly, 311 boolean ignoreMetadata) { 312 super.setInput(input, seekForwardOnly, ignoreMetadata); 313 this.ignoreMetadata = ignoreMetadata; 314 iis = (ImageInputStream) input; // Always works 315 imageMetadata = null; 316 try { 317 this.streamPosition0 = iis.getStreamPosition(); 318 } catch(IOException e) { 319 // XXX ignore 320 } 321 } 322 323 /** Overrides the method defined in the superclass. */ 324 public int getNumImages(boolean allowSearch) throws IOException { 325 return 1; 326 } 327 328 public int getWidth(int imageIndex) throws IOException { 329 checkIndex(imageIndex); 330 readHeader(); 331 return width; 332 } 333 334 public int getHeight(int imageIndex) throws IOException { 335 checkIndex(imageIndex); 336 readHeader(); 337 return height; 338 } 339 340 public int getTileGridXOffset(int imageIndex) throws IOException { 341 checkIndex(imageIndex); 342 readHeader(); 343 return hd.getTilingOrigin(null).x; 344 } 345 346 public int getTileGridYOffset(int imageIndex) throws IOException { 347 checkIndex(imageIndex); 348 readHeader(); 349 return hd.getTilingOrigin(null).y; 350 } 351 352 public int getTileWidth(int imageIndex) throws IOException { 353 checkIndex(imageIndex); 354 readHeader(); 355 return hd.getNomTileWidth(); 356 } 357 358 public int getTileHeight(int imageIndex) throws IOException { 359 checkIndex(imageIndex); 360 readHeader(); 361 return hd.getNomTileHeight(); 362 } 363 364 private void checkIndex(int imageIndex) { 365 if (imageIndex != 0) { 366 throw new IndexOutOfBoundsException(I18N.getString("J2KImageReader4")); 367 } 368 } 369 370 public void readHeader() { 371 if (gotHeader) 372 return; 373 374 if (readState == null) { 375 try { 376 iis.seek(streamPosition0); 377 } catch(IOException e) { 378 // XXX ignore 379 } 380 381 readState = 382 new J2KReadState(iis, 383 new J2KImageReadParamJava(getDefaultReadParam()), 384 this); 385 } 386 387 hd = readState.getHeader(); 388 gotHeader = true; 389 390 this.width = hd.getImgWidth(); 391 this.height = hd.getImgHeight(); 392 } 393 394 public Iterator getImageTypes(int imageIndex) 395 throws IOException { 396 checkIndex(imageIndex); 397 readHeader(); 398 if (readState != null) { 399 ArrayList list = new ArrayList(); 400 list.add(new ImageTypeSpecifier(readState.getColorModel(), 401 readState.getSampleModel())); 402 return list.iterator(); 403 } 404 return null; 405 } 406 407 public ImageReadParam getDefaultReadParam() { 408 return new J2KImageReadParam(); 409 } 410 411 public IIOMetadata getImageMetadata(int imageIndex) 412 throws IOException { 413 checkIndex(imageIndex); 414 if (ignoreMetadata) 415 return null; 416 417 if (imageMetadata == null) { 418 iis.mark(); 419 imageMetadata = new J2KMetadata(iis, this); 420 iis.reset(); 421 } 422 return imageMetadata; 423 } 424 425 public IIOMetadata getStreamMetadata() throws IOException { 426 return null; 427 } 428 429 public BufferedImage read(int imageIndex, ImageReadParam param) 430 throws IOException { 431 checkIndex(imageIndex); 432 clearAbortRequest(); 433 processImageStarted(imageIndex); 434 435 if (param == null) 436 param = getDefaultReadParam(); 437 438 param = new J2KImageReadParamJava(param); 439 440 if (!ignoreMetadata) { 441 imageMetadata = new J2KMetadata(); 442 iis.seek(streamPosition0); 443 readState = new J2KReadState(iis, 444 (J2KImageReadParamJava)param, 445 imageMetadata, 446 this); 447 } else { 448 iis.seek(streamPosition0); 449 readState = new J2KReadState(iis, 450 (J2KImageReadParamJava)param, 451 this); 452 } 453 454 BufferedImage bi = readState.readBufferedImage(); 455 if (abortRequested()) 456 processReadAborted(); 457 else 458 processImageComplete(); 459 return bi; 460 } 461 462 public RenderedImage readAsRenderedImage(int imageIndex, 463 ImageReadParam param) 464 throws IOException { 465 checkIndex(imageIndex); 466 RenderedImage ri = null; 467 clearAbortRequest(); 468 processImageStarted(imageIndex); 469 470 if (param == null) 471 param = getDefaultReadParam(); 472 473 param = new J2KImageReadParamJava(param); 474 if (!ignoreMetadata) { 475 if (imageMetadata == null) 476 imageMetadata = new J2KMetadata(); 477 ri = new J2KRenderedImage(iis, 478 (J2KImageReadParamJava)param, 479 imageMetadata, 480 this); 481 } 482 else 483 ri = new J2KRenderedImage(iis, (J2KImageReadParamJava)param, this); 484 if (abortRequested()) 485 processReadAborted(); 486 else 487 processImageComplete(); 488 return ri; 489 } 490 491 public boolean canReadRaster() { 492 return true; 493 } 494 495 public boolean isRandomAccessEasy(int imageIndex) throws IOException { 496 checkIndex(imageIndex); 497 return false; 498 } 499 500 public Raster readRaster(int imageIndex, 501 ImageReadParam param) throws IOException { 502 checkIndex(imageIndex); 503 processImageStarted(imageIndex); 504 param = new J2KImageReadParamJava(param); 505 506 if (!ignoreMetadata) { 507 imageMetadata = new J2KMetadata(); 508 iis.seek(streamPosition0); 509 readState = new J2KReadState(iis, 510 (J2KImageReadParamJava)param, 511 imageMetadata, 512 this); 513 } else { 514 iis.seek(streamPosition0); 515 readState = new J2KReadState(iis, 516 (J2KImageReadParamJava)param, 517 this); 518 } 519 520 Raster ras = readState.readAsRaster(); 521 if (abortRequested()) 522 processReadAborted(); 523 else 524 processImageComplete(); 525 return ras; 526 } 527 528 public boolean isImageTiled(int imageIndex) { 529 checkIndex(imageIndex); 530 readHeader(); 531 if (readState != null) { 532 RenderedImage image = new J2KRenderedImage(readState); 533 if (image.getNumXTiles() * image.getNumYTiles() > 0) 534 return true; 535 return false; 536 } 537 return false; 538 } 539 540 public void reset() { 541 // reset local Java structures 542 super.reset(); 543 544 iis = null; 545 gotHeader = false; 546 imageMetadata = null; 547 readState = null; 548 System.gc(); 549 } 550 551 /** This method wraps the protected method <code>abortRequested</code> 552 * to allow the abortions be monitored by <code>J2KReadState</code>. 553 */ 554 public boolean getAbortRequest() { 555 return abortRequested(); 556 } 557 558 private ImageTypeSpecifier getImageType(int imageIndex) 559 throws IOException { 560 checkIndex(imageIndex); 561 readHeader(); 562 if (readState != null) { 563 return new ImageTypeSpecifier(readState.getColorModel(), 564 readState.getSampleModel()); 565 } 566 return null; 567 } 568 569 // --- Begin jj2000.j2k.util.MsgLogger implementation --- 570 public void flush() { 571 // Do nothing. 572 } 573 574 public void println(String str, int flind, int ind) { 575 printmsg(INFO, str); 576 } 577 578 public void printmsg(int sev, String msg) { 579 if(logJJ2000Msg) { 580 String msgSev; 581 switch(sev) { 582 case ERROR: 583 msgSev = "ERROR"; 584 break; 585 case INFO: 586 msgSev = "INFO"; 587 break; 588 case LOG: 589 msgSev = "LOG"; 590 break; 591 case WARNING: 592 default: 593 msgSev = "WARNING"; 594 break; 595 } 596 597 processWarningOccurred("[JJ2000 "+msgSev+"] "+msg); 598 } 599 } 600 // --- End jj2000.j2k.util.MsgLogger implementation --- 601}