001/* 002 * $RCSfile: J2KReadState.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.8 $ 042 * $Date: 2006/10/03 23:40:14 $ 043 * $State: Exp $ 044 */ 045package com.sun.media.imageioimpl.plugins.jpeg2000; 046 047import javax.imageio.IIOException; 048import javax.imageio.ImageReader; 049import javax.imageio.ImageReadParam; 050import javax.imageio.ImageTypeSpecifier; 051import javax.imageio.metadata.IIOMetadata; 052import javax.imageio.spi.ImageReaderSpi; 053import javax.imageio.stream.ImageInputStream; 054 055import java.awt.Point; 056import java.awt.Rectangle; 057import java.awt.Transparency; 058import java.awt.color.ColorSpace; 059import java.awt.image.BufferedImage; 060import java.awt.image.DataBuffer; 061import java.awt.image.DataBufferByte; 062import java.awt.image.ColorModel; 063import java.awt.image.ComponentColorModel; 064import java.awt.image.ComponentSampleModel; 065import java.awt.image.DirectColorModel; 066import java.awt.image.IndexColorModel; 067import java.awt.image.MultiPixelPackedSampleModel; 068import java.awt.image.PixelInterleavedSampleModel; 069import java.awt.image.Raster; 070import java.awt.image.RenderedImage; 071import java.awt.image.SampleModel; 072import java.awt.image.SinglePixelPackedSampleModel; 073import java.awt.image.WritableRaster; 074 075import java.io.*; 076import java.util.ArrayList; 077import java.util.List; 078import java.util.Hashtable; 079import java.util.Iterator; 080 081import jj2000.j2k.quantization.dequantizer.*; 082import jj2000.j2k.wavelet.synthesis.*; 083import jj2000.j2k.image.invcomptransf.*; 084import jj2000.j2k.fileformat.reader.*; 085import jj2000.j2k.codestream.reader.*; 086import jj2000.j2k.entropy.decoder.*; 087import jj2000.j2k.codestream.*; 088import jj2000.j2k.decoder.*; 089import jj2000.j2k.image.*; 090import jj2000.j2k.util.*; 091import jj2000.j2k.roi.*; 092import jj2000.j2k.io.*; 093import jj2000.j2k.*; 094 095import com.sun.media.imageioimpl.common.ImageUtil; 096 097public class J2KReadState { 098 /** The input stream we read from */ 099 private ImageInputStream iis = null; 100 101 private FileFormatReader ff; 102 private HeaderInfo hi; 103 private HeaderDecoder hd; 104 private RandomAccessIO in; 105 private BitstreamReaderAgent breader; 106 private EntropyDecoder entdec; 107 private ROIDeScaler roids; 108 private Dequantizer deq; 109 private InverseWT invWT; 110 private InvCompTransf ictransf; 111 private ImgDataConverter converter,converter2; 112 private DecoderSpecs decSpec = null; 113 private J2KImageReadParamJava j2krparam = null; 114 private int[] destinationBands = null; 115 private int[] sourceBands = null; 116 117 private int[] levelShift = null; // level shift for each component 118 private int[] minValues = null; // The min values 119 private int[] maxValues = null; // The max values 120 private int[] fracBits = null; // fractional bits for each component 121 private DataBlkInt[] dataBlocks = null; // data-blocks to request data from src 122 123 private int[] bandOffsets = null; 124 private int maxDepth = 0; 125 private boolean isSigned = false; 126 127 private ColorModel colorModel = null; 128 private SampleModel sampleModel = null; 129 private int nComp = 0; 130 private int tileWidth = 0; 131 private int tileHeight = 0; 132 133 /** Source to destination transform */ 134 private int scaleX, scaleY, xOffset, yOffset; 135 private Rectangle destinationRegion = null; 136 private Point sourceOrigin; 137 138 /** Tile grid offsets of the source, also used for destination. */ 139 private int tileXOffset, tileYOffset; 140 141 private int width; 142 private int height; 143 private int[] pixbuf = null; 144 private byte[] bytebuf = null; 145 private int[] channelMap = null; 146 147 private boolean noTransform = true; 148 149 /** The resolution level requested. */ 150 private int resolution; 151 152 /** The subsampling step sizes. */ 153 private int stepX, stepY; 154 155 /** Tile step sizes. */ 156 private int tileStepX, tileStepY; 157 158 private J2KMetadata metadata; 159 160 private BufferedImage destImage; 161 162 /** Cache the <code>J2KImageReader</code> which creates this object. This 163 * variable is used to monitor the abortion. 164 */ 165 private J2KImageReader reader; 166 167 /** Constructs <code>J2KReadState</code>. 168 * @param iis The input stream. 169 * @param param The reading parameters. 170 * @param metadata The <code>J2KMetadata</code> to cache the metadata read 171 * from the input stream. 172 * @param reader The <code>J2KImageReader</code> which holds this state. 173 * It is necessary for processing abortion. 174 * @throw IllegalArgumentException If the provided <code>iis</code>, 175 * <code>param</code> or <code>metadata</code> is <code>null</code>. 176 */ 177 public J2KReadState(ImageInputStream iis, 178 J2KImageReadParamJava param, 179 J2KMetadata metadata, 180 J2KImageReader reader) { 181 if (iis == null || param == null || metadata == null) 182 throw new IllegalArgumentException(I18N.getString("J2KReadState0")); 183 184 this.iis = iis; 185 this.j2krparam = param; 186 this.metadata = metadata; 187 this.reader = reader; 188 189 initializeRead(0, param, metadata); 190 } 191 192 /** Constructs <code>J2KReadState</code>. 193 * @param iis The input stream. 194 * @param param The reading parameters. 195 * @param reader The <code>J2KImageReader</code> which holds this state. 196 * It is necessary for processing abortion. 197 * @throw IllegalArgumentException If the provided <code>iis</code>, 198 * or <code>param</code> is <code>null</code>. 199 */ 200 public J2KReadState(ImageInputStream iis, 201 J2KImageReadParamJava param, 202 J2KImageReader reader) { 203 if (iis == null || param == null) 204 throw new IllegalArgumentException(I18N.getString("J2KReadState0")); 205 206 this.iis = iis; 207 this.j2krparam = param; 208 this.reader = reader; 209 initializeRead(0, param, null); 210 } 211 212 public int getWidth() throws IOException { 213 return width; 214 } 215 216 public int getHeight() throws IOException { 217 return height; 218 } 219 220 public HeaderDecoder getHeader() { 221 return hd; 222 } 223 224 public Raster getTile(int tileX, int tileY, 225 WritableRaster raster) throws IOException { 226 Point nT = ictransf.getNumTiles(null); 227 228 if (noTransform) { 229 if (tileX >= nT.x || tileY >= nT.y) 230 throw new IllegalArgumentException(I18N.getString("J2KImageReader0")); 231 232 ictransf.setTile(tileX*tileStepX, tileY*tileStepY); 233 234 // The offset of the active tiles is the same for all components, 235 // since we don't support different component dimensions. 236 int tOffx; 237 int tOffy; 238 int cTileWidth; 239 int cTileHeight; 240 if(raster != null && 241 (this.resolution < hd.getDecoderSpecs().dls.getMin()) || 242 stepX != 1 || stepY != 1) { 243 tOffx = raster.getMinX(); 244 tOffy = raster.getMinY(); 245 cTileWidth = Math.min(raster.getWidth(), 246 ictransf.getTileWidth()); 247 cTileHeight = Math.min(raster.getHeight(), 248 ictransf.getTileHeight()); 249 } else { 250 tOffx = ictransf.getCompULX(0) - 251 (ictransf.getImgULX() + ictransf.getCompSubsX(0) - 1) / 252 ictransf.getCompSubsX(0) + destinationRegion.x; 253 tOffy = ictransf.getCompULY(0)- 254 (ictransf.getImgULY() + ictransf.getCompSubsY(0) - 1) / 255 ictransf.getCompSubsY(0) + destinationRegion.y; 256 cTileWidth = ictransf.getTileWidth(); 257 cTileHeight = ictransf.getTileHeight(); 258 } 259 260 if (raster == null) 261 raster = Raster.createWritableRaster(sampleModel, 262 new Point(tOffx, tOffy)); 263 264 int numBands = sampleModel.getNumBands(); 265 266 if (tOffx + cTileWidth >= 267 destinationRegion.width + destinationRegion.x) 268 cTileWidth = 269 destinationRegion.width + destinationRegion.x - tOffx; 270 271 if (tOffy + cTileHeight >= 272 destinationRegion.height + destinationRegion.y) 273 cTileHeight = 274 destinationRegion.height + destinationRegion.y - tOffy; 275 276 //create the line buffer for pixel data if it is not large enough 277 // or null 278 if (pixbuf == null || pixbuf.length < cTileWidth * numBands) 279 pixbuf = new int[cTileWidth * numBands]; 280 boolean prog = false; 281 282 // Deliver in lines to reduce memory usage 283 for (int l=0; l < cTileHeight;l++) { 284 if (reader.getAbortRequest()) 285 break; 286 287 // Request line data 288 for (int i = 0; i < numBands; i++) { 289 if (reader.getAbortRequest()) 290 break; 291 DataBlkInt db = dataBlocks[i]; 292 db.ulx = 0; 293 db.uly = l; 294 db.w = cTileWidth; 295 db.h = 1; 296 ictransf.getInternCompData(db, channelMap[sourceBands[i]]); 297 prog = prog || db.progressive; 298 299 int[] data = db.data; 300 int k1 = db.offset + cTileWidth - 1; 301 302 int fracBit = fracBits[i]; 303 int lS = levelShift[i]; 304 int min = minValues[i]; 305 int max = maxValues[i]; 306 307 if (ImageUtil.isBinary(sampleModel)) { 308 // Force min max to 0 and 1. 309 min = 0; 310 max = 1; 311 if (bytebuf == null || bytebuf.length < cTileWidth * numBands) 312 bytebuf = new byte[cTileWidth * numBands]; 313 for (int j = cTileWidth - 1; 314 j >= 0; j--) { 315 int tmp = (data[k1--] >> fracBit) + lS; 316 bytebuf[j] = 317 (byte)((tmp < min) ? min : 318 ((tmp > max) ? max : tmp)); 319 } 320 321 ImageUtil.setUnpackedBinaryData(bytebuf, 322 raster, 323 new Rectangle(tOffx, 324 tOffy + l, 325 cTileWidth, 326 1)); 327 } else { 328 329 for (int j = cTileWidth - 1; 330 j >= 0; j--) { 331 int tmp = (data[k1--] >> fracBit) + lS; 332 pixbuf[j] = (tmp < min) ? min : 333 ((tmp > max) ? max : tmp); 334 } 335 336 raster.setSamples(tOffx, 337 tOffy + l, 338 cTileWidth, 339 1, 340 destinationBands[i], 341 pixbuf); 342 } 343 } 344 } 345 } else { 346 readSubsampledRaster(raster); 347 } 348 349 return raster; 350 } 351 352 public Rectangle getDestinationRegion() { 353 return destinationRegion; 354 } 355 356 public BufferedImage readBufferedImage() throws IOException { 357 colorModel = getColorModel(); 358 sampleModel = getSampleModel(); 359 WritableRaster raster = null; 360 BufferedImage image = j2krparam.getDestination(); 361 362 int x = destinationRegion.x; 363 int y = destinationRegion.y; 364 destinationRegion.setLocation(j2krparam.getDestinationOffset()); 365 if (image == null) { 366 // If the destination type is specified, use the color model of it. 367 ImageTypeSpecifier type = j2krparam.getDestinationType(); 368 if (type != null) 369 colorModel = type.getColorModel(); 370 371 raster = Raster.createWritableRaster( 372 sampleModel.createCompatibleSampleModel(destinationRegion.x + 373 destinationRegion.width, 374 destinationRegion.y + 375 destinationRegion.height), 376 new Point(0, 0)); 377 image = new BufferedImage(colorModel, raster, 378 colorModel.isAlphaPremultiplied(), 379 new Hashtable()); 380 } else 381 raster = image.getWritableTile(0, 0); 382 383 destImage = image; 384 readSubsampledRaster(raster); 385 destinationRegion.setLocation(x, y); 386 destImage = null; 387 return image; 388 } 389 390 public Raster readAsRaster() throws IOException { 391 BufferedImage image = j2krparam.getDestination(); 392 WritableRaster raster = null; 393 394 if (image == null) { 395 raster = Raster.createWritableRaster( 396 sampleModel.createCompatibleSampleModel(destinationRegion.x + 397 destinationRegion.width, 398 destinationRegion.y + 399 destinationRegion.height), 400 new Point(0, 0)); 401 } else 402 raster = image.getWritableTile(0, 0); 403 404 readSubsampledRaster(raster); 405 return raster; 406 } 407 408 private void initializeRead(int imageIndex, J2KImageReadParamJava param, 409 J2KMetadata metadata) { 410 try { 411 iis.mark(); 412 in = new IISRandomAccessIO(iis); 413 414 // **** File Format **** 415 // If the codestream is wrapped in the jp2 fileformat, Read the 416 // file format wrapper 417 ff = new FileFormatReader(in, metadata); 418 ff.readFileFormat(); 419 in.seek(ff.getFirstCodeStreamPos()); 420 421 hi = new HeaderInfo(); 422 try{ 423 hd = new HeaderDecoder(in, j2krparam, hi); 424 } catch(EOFException e){ 425 throw new RuntimeException(I18N.getString("J2KReadState2")); 426 } catch (IOException ioe) { 427 throw new RuntimeException(ioe); 428 } 429 430 this.width = hd.getImgWidth(); 431 this.height = hd.getImgHeight(); 432 433 Rectangle sourceRegion = param.getSourceRegion(); 434 sourceOrigin = new Point(); 435 sourceRegion = 436 new Rectangle(hd.getImgULX(), hd.getImgULY(), 437 this.width, this.height); 438 439 // if the subsample rate for components are not consistent 440 boolean compConsistent = true; 441 stepX = hd.getCompSubsX(0); 442 stepY = hd.getCompSubsY(0); 443 for (int i = 1; i < nComp; i++) { 444 if (stepX != hd.getCompSubsX(i) || stepY != hd.getCompSubsY(i)) 445 throw new RuntimeException(I18N.getString("J2KReadState12")); 446 } 447 448 // Get minimum number of resolution levels available across 449 // all tile-components. 450 int minResLevels = hd.getDecoderSpecs().dls.getMin(); 451 452 // Set current resolution level. 453 this.resolution = param != null ? 454 param.getResolution() : minResLevels; 455 if(resolution < 0 || resolution > minResLevels) { 456 resolution = minResLevels; 457 } 458 459 // Convert source region to lower resolution level. 460 if(resolution != minResLevels || stepX != 1 || stepY != 1) { 461 sourceRegion = 462 J2KImageReader.getReducedRect(sourceRegion, minResLevels, 463 resolution, stepX, stepY); 464 } 465 466 destinationRegion = (Rectangle)sourceRegion.clone(); 467 468 J2KImageReader.computeRegionsWrapper(param, 469 false, 470 this.width, 471 this.height, 472 param.getDestination(), 473 sourceRegion, 474 destinationRegion); 475 476 sourceOrigin = new Point(sourceRegion.x, sourceRegion.y); 477 scaleX = param.getSourceXSubsampling(); 478 scaleY = param.getSourceYSubsampling(); 479 xOffset = param.getSubsamplingXOffset(); 480 yOffset = param.getSubsamplingYOffset(); 481 482 this.width = destinationRegion.width; 483 this.height = destinationRegion.height; 484 485 Point tileOffset = hd.getTilingOrigin(null); 486 487 this.tileWidth = hd.getNomTileWidth(); 488 this.tileHeight = hd.getNomTileHeight(); 489 490 // Convert tile 0 to lower resolution level. 491 if(resolution != minResLevels || stepX != 1 || stepY != 1) { 492 Rectangle tileRect = new Rectangle(tileOffset); 493 tileRect.width = tileWidth; 494 tileRect.height = tileHeight; 495 tileRect = 496 J2KImageReader.getReducedRect(tileRect, minResLevels, 497 resolution, stepX, stepY); 498 tileOffset = tileRect.getLocation(); 499 tileWidth = tileRect.width; 500 tileHeight = tileRect.height; 501 } 502 503 tileXOffset = tileOffset.x; 504 tileYOffset = tileOffset.y; 505 506 507 // Set the tile step sizes. These values are used because it 508 // is possible that tiles will be empty. In particular at lower 509 // resolution levels when subsampling is used this may be the 510 // case. This method of calculation will work at least for 511 // Profile-0 images. 512 if(tileWidth*(1 << (minResLevels - resolution))*stepX > 513 hd.getNomTileWidth()) { 514 tileStepX = 515 (tileWidth*(1 << (minResLevels - resolution))*stepX + 516 hd.getNomTileWidth() - 1)/hd.getNomTileWidth(); 517 } else { 518 tileStepX = 1; 519 } 520 521 if(tileHeight*(1 << (minResLevels - resolution))*stepY > 522 hd.getNomTileHeight()) { 523 tileStepY = 524 (tileHeight*(1 << (minResLevels - resolution))*stepY + 525 hd.getNomTileHeight() - 1)/hd.getNomTileHeight(); 526 } else { 527 tileStepY = 1; 528 } 529 530 if (!destinationRegion.equals(sourceRegion)) 531 noTransform = false; 532 533 // **** Header decoder **** 534 // Instantiate header decoder and read main header 535 decSpec = hd.getDecoderSpecs(); 536 537 // **** Instantiate decoding chain **** 538 // Get demixed bitdepths 539 nComp = hd.getNumComps(); 540 541 int[] depth = new int[nComp]; 542 for (int i=0; i<nComp;i++) 543 depth[i] = hd.getOriginalBitDepth(i); 544 545 //Get channel mapping 546 ChannelDefinitionBox cdb = null; 547 if (metadata != null) 548 cdb = (ChannelDefinitionBox)metadata.getElement("JPEG2000ChannelDefinitionBox"); 549 550 channelMap = new int[nComp]; 551 if (cdb != null && 552 metadata.getElement("JPEG2000PaletteBox") == null) { 553 short[] assoc = cdb.getAssociation(); 554 short[] types = cdb.getTypes(); 555 short[] channels = cdb.getChannel(); 556 557 for (int i = 0; i < types.length; i++) 558 if (types[i] == 0) 559 channelMap[channels[i]] = assoc[i] - 1; 560 else if (types[i] == 1 || types[i] == 2) 561 channelMap[channels[i]] = channels[i]; 562 } else { 563 for (int i = 0; i < nComp; i++) 564 channelMap[i] = i; 565 } 566 567 // **** Bitstream reader **** 568 try { 569 boolean logJJ2000Messages = 570 Boolean.getBoolean("jj2000.j2k.decoder.log"); 571 breader = 572 BitstreamReaderAgent.createInstance(in, hd, 573 j2krparam, decSpec, 574 logJJ2000Messages, hi); 575 } catch (IOException e) { 576 throw new RuntimeException(I18N.getString("J2KReadState3") + " " + 577 ((e.getMessage() != null) ? 578 (":\n"+e.getMessage()) : "")); 579 } catch (IllegalArgumentException e) { 580 throw new RuntimeException(I18N.getString("J2KReadState4") + " " + 581 ((e.getMessage() != null) ? 582 (":\n"+e.getMessage()) : "")); 583 } 584 585 // **** Entropy decoder **** 586 try { 587 entdec = hd.createEntropyDecoder(breader, j2krparam); 588 } catch (IllegalArgumentException e) { 589 throw new RuntimeException(I18N.getString("J2KReadState5") + " " + 590 ((e.getMessage() != null) ? 591 (":\n"+e.getMessage()) : "")); 592 } 593 594 // **** ROI de-scaler **** 595 try { 596 roids = hd.createROIDeScaler(entdec, j2krparam, decSpec); 597 } catch (IllegalArgumentException e) { 598 throw new RuntimeException(I18N.getString("J2KReadState6") + " " + 599 ((e.getMessage() != null) ? 600 (":\n"+e.getMessage()) : "")); 601 } 602 603 604 // **** Dequantizer **** 605 try { 606 deq = hd.createDequantizer(roids, depth, decSpec); 607 } catch (IllegalArgumentException e) { 608 throw new RuntimeException(I18N.getString("J2KReadState7") + " " + 609 ((e.getMessage() != null) ? 610 (":\n"+e.getMessage()) : "")); 611 } 612 613 // **** Inverse wavelet transform *** 614 try { 615 // full page inverse wavelet transform 616 invWT = InverseWT.createInstance(deq,decSpec); 617 } catch (IllegalArgumentException e) { 618 throw new RuntimeException(I18N.getString("J2KReadState8") + " " + 619 ((e.getMessage() != null) ? 620 (":\n"+e.getMessage()) : "")); 621 } 622 623 int res = breader.getImgRes(); 624 int mrl = decSpec.dls.getMin(); 625 invWT.setImgResLevel(res); 626 627 // **** Data converter **** (after inverse transform module) 628 converter = new ImgDataConverter(invWT,0); 629 630 // **** Inverse component transformation **** 631 ictransf = new InvCompTransf(converter, decSpec, depth); 632 633 // If the destination band is set used it 634 sourceBands = j2krparam.getSourceBands(); 635 636 if (sourceBands == null) { 637 sourceBands = new int[nComp]; 638 for (int i = 0; i < nComp; i++) 639 sourceBands[i] = i; 640 } 641 642 nComp = sourceBands.length; 643 644 destinationBands = j2krparam.getDestinationBands(); 645 if (destinationBands == null) { 646 destinationBands = new int[nComp]; 647 for (int i = 0; i < nComp; i++) 648 destinationBands[i] = i; 649 } 650 651 J2KImageReader.checkReadParamBandSettingsWrapper(param, 652 hd.getNumComps(), 653 destinationBands.length); 654 655 levelShift = new int[nComp]; 656 minValues = new int[nComp]; 657 maxValues = new int[nComp]; 658 fracBits = new int[nComp]; 659 dataBlocks = new DataBlkInt[nComp]; 660 661 depth = new int[nComp]; 662 bandOffsets = new int[nComp]; 663 maxDepth = 0; 664 isSigned = false; 665 for (int i=0; i<nComp;i++) { 666 depth[i] = hd.getOriginalBitDepth(sourceBands[i]); 667 if (depth[i] > maxDepth) 668 maxDepth = depth[i]; 669 dataBlocks[i] = new DataBlkInt(); 670 671 //XXX: may need to change if ChannelDefinition is used to 672 // define the color channels, such as BGR order 673 bandOffsets[i] = i; 674 if (hd.isOriginalSigned(sourceBands[i])) 675 isSigned = true; 676 else { 677 levelShift[i] = 678 1<<(ictransf.getNomRangeBits(sourceBands[i])-1); 679 } 680 681 // Get the number of bits in the image, and decide what the max 682 // value should be, depending on whether it is signed or not 683 int nomRangeBits = ictransf.getNomRangeBits(sourceBands[i]); 684 maxValues[i] = (1 << (isSigned == true ? (nomRangeBits-1) : 685 nomRangeBits)) - 1; 686 minValues[i] = isSigned ? -(maxValues[i]+1) : 0; 687 688 fracBits[i] = ictransf.getFixedPoint(sourceBands[i]); 689 } 690 691 iis.reset(); 692 } catch (IllegalArgumentException e){ 693 throw new RuntimeException(e.getMessage(), e); 694 } catch (Error e) { 695 if(e.getMessage()!=null) 696 throw new RuntimeException(e.getMessage(), e); 697 else { 698 throw new RuntimeException(I18N.getString("J2KReadState9"), e); 699 } 700 } catch (RuntimeException e) { 701 if(e.getMessage()!=null) 702 throw new RuntimeException(I18N.getString("J2KReadState10") + " " + 703 e.getMessage(), e); 704 else { 705 throw new RuntimeException(I18N.getString("J2KReadState10"), e); 706 } 707 } catch (Throwable e) { 708 throw new RuntimeException(I18N.getString("J2KReadState10"), e); 709 } 710 } 711 712 private Raster readSubsampledRaster(WritableRaster raster) throws IOException { 713 if (raster == null) 714 raster = Raster.createWritableRaster( 715 sampleModel.createCompatibleSampleModel(destinationRegion.x + 716 destinationRegion.width, 717 destinationRegion.y + 718 destinationRegion.height), 719 new Point(destinationRegion.x, destinationRegion.y)); 720 721 int pixbuf[] = null; // line buffer for pixel data 722 boolean prog = false; // Flag for progressive data 723 Point nT = ictransf.getNumTiles(null); 724 int numBands = sourceBands.length; 725 726 Rectangle destRect = raster.getBounds().intersection(destinationRegion); 727 728 int offx = destinationRegion.x; 729 int offy = destinationRegion.y; 730 731 int sourceSX = (destRect.x - offx) * scaleX + sourceOrigin.x; 732 int sourceSY = (destRect.y - offy) * scaleY + sourceOrigin.y; 733 int sourceEX = (destRect.width - 1)* scaleX + sourceSX; 734 int sourceEY = (destRect.height - 1) * scaleY + sourceSY; 735 736 int startXTile = (sourceSX - tileXOffset) / tileWidth; 737 int startYTile = (sourceSY - tileYOffset) / tileHeight; 738 int endXTile = (sourceEX - tileXOffset) / tileWidth; 739 int endYTile = (sourceEY - tileYOffset) / tileHeight; 740 741 startXTile = clip(startXTile, 0, nT.x - 1); 742 startYTile = clip(startYTile, 0, nT.y - 1); 743 endXTile = clip(endXTile, 0, nT.x - 1); 744 endYTile = clip(endYTile, 0, nT.y - 1); 745 746 int totalXTiles = endXTile - startXTile + 1; 747 int totalYTiles = endYTile - startYTile + 1; 748 int totalTiles = totalXTiles * totalYTiles; 749 750 // Start the data delivery to the cached consumers tile by tile 751 for(int y=startYTile; y <= endYTile; y++){ 752 if (reader.getAbortRequest()) 753 break; 754 755 // Loop on horizontal tiles 756 for(int x=startXTile; x <= endXTile; x++){ 757 if (reader.getAbortRequest()) 758 break; 759 760 float initialFraction = 761 (x - startXTile + (y - startYTile)*totalXTiles)/totalTiles; 762 763 ictransf.setTile(x*tileStepX,y*tileStepY); 764 765 int sx = hd.getCompSubsX(0); 766 int cTileWidth = (ictransf.getTileWidth() + sx - 1)/sx; 767 int sy = hd.getCompSubsY(0); 768 int cTileHeight = (ictransf.getTileHeight() + sy - 1)/sy; 769 770 // Offsets within the tile. 771 int tx = 0; 772 int ty = 0; 773 774 // The region for this tile 775 int startX = tileXOffset + x * tileWidth; 776 int startY = tileYOffset + y * tileHeight; 777 778 // sourceSX is guaranteed to be >= startX 779 if (sourceSX > startX) { 780 if(startX >= hd.getImgULX()) { 781 tx = sourceSX - startX; // Intra-tile offset. 782 cTileWidth -= tx; // Reduce effective width. 783 } 784 startX = sourceSX; // Absolute position. 785 } 786 787 // sourceSY is guaranteed to be >= startY 788 if (sourceSY > startY) { 789 if(startY >= hd.getImgULY()) { 790 ty = sourceSY - startY; // Intra-tile offset. 791 cTileHeight -= ty; // Reduce effective width. 792 } 793 startY = sourceSY; // Absolute position. 794 } 795 796 // Decrement dimensions if end position is within tile. 797 if (sourceEX < startX + cTileWidth - 1) { 798 cTileWidth += sourceEX - startX - cTileWidth + 1; 799 } 800 if (sourceEY < startY + cTileHeight - 1) { 801 cTileHeight += sourceEY - startY - cTileHeight + 1; 802 } 803 804 // The start X in the destination 805 int x1 = (startX + scaleX - 1 - sourceOrigin.x) / scaleX; 806 int x2 = (startX + scaleX -1 + cTileWidth - sourceOrigin.x) / 807 scaleX; 808 int lineLength = x2 - x1; 809 if (pixbuf == null || pixbuf.length < lineLength) 810 pixbuf = new int[lineLength]; // line buffer for pixel data 811 x2 = (x2 - 1) * scaleX + sourceOrigin.x - startX; 812 813 int y1 = (startY + scaleY -1 - sourceOrigin.y) /scaleY; 814 815 x1 += offx; 816 y1 += offy; 817 818 // Deliver in lines to reduce memory usage 819 for (int l = ty, m = y1; 820 l < ty + cTileHeight; 821 l += scaleY, m++) { 822 if (reader.getAbortRequest()) 823 break; 824 // Request line data 825 for (int i = 0; i < numBands; i++) { 826 DataBlkInt db = dataBlocks[i]; 827 db.ulx = tx; 828 db.uly = l; 829 db.w = cTileWidth; 830 db.h = 1; 831 ictransf.getInternCompData(db, channelMap[sourceBands[i]]); 832 prog = prog || db.progressive; 833 834 int[] data = db.data; 835 int k1 = db.offset + x2; 836 837 int fracBit = fracBits[i]; 838 int lS = levelShift[i]; 839 int min = minValues[i]; 840 int max = maxValues[i]; 841 842 if (ImageUtil.isBinary(sampleModel)) { 843 // Force min max to 0 and 1. 844 min = 0; 845 max = 1; 846 if (bytebuf == null || bytebuf.length < cTileWidth * numBands) 847 bytebuf = new byte[cTileWidth * numBands]; 848 for (int j = lineLength - 1; j >= 0; j--, k1-=scaleX) { 849 int tmp = (data[k1] >> fracBit) + lS; 850 bytebuf[j] = 851 (byte)((tmp < min) ? min : 852 ((tmp > max) ? max : tmp)); 853 } 854 855 ImageUtil.setUnpackedBinaryData(bytebuf, 856 raster, 857 new Rectangle(x1, 858 m, 859 lineLength, 860 1)); 861 } else { 862 for (int j = lineLength - 1; j >= 0; j--, k1-=scaleX) { 863 int tmp = (data[k1] >> fracBit) + lS; 864 pixbuf[j] = (tmp < min) ? min : 865 ((tmp > max) ? max : tmp); 866 } 867 868 // Send the line data to the BufferedImage 869 raster.setSamples(x1, 870 m, 871 lineLength, 872 1, 873 destinationBands[i], 874 pixbuf); 875 } 876 } 877 878 if (destImage != null) 879 reader.processImageUpdateWrapper(destImage, x1, m, 880 cTileWidth, 1, 1, 1, 881 destinationBands); 882 883 float fraction = initialFraction + 884 (l - ty + 1.0F)/cTileHeight/totalTiles; 885 reader.processImageProgressWrapper(100.0f*fraction); 886 } 887 } // End loop on horizontal tiles 888 } // End loop on vertical tiles 889 890 return raster; 891 } 892 893 public ImageTypeSpecifier getImageType() 894 throws IOException { 895 896 getSampleModel(); 897 getColorModel(); 898 899 return new ImageTypeSpecifier(colorModel, sampleModel); 900 } 901 902 public SampleModel getSampleModel() { 903 if (sampleModel != null) 904 return sampleModel; 905 906 if (nComp == 1 && (maxDepth == 1 || maxDepth == 2 || maxDepth == 4)) 907 sampleModel = 908 new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 909 tileWidth, 910 tileHeight, 911 maxDepth); 912 else if (maxDepth <= 8) 913 sampleModel = 914 new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 915 tileWidth, 916 tileHeight, 917 nComp, 918 tileWidth * nComp, 919 bandOffsets); 920 else if (maxDepth <=16) 921 sampleModel = 922 new PixelInterleavedSampleModel(isSigned ? 923 DataBuffer.TYPE_SHORT : 924 DataBuffer.TYPE_USHORT, 925 tileWidth, tileHeight, 926 nComp, 927 tileWidth * nComp, 928 bandOffsets); 929 else if (maxDepth <= 32) 930 sampleModel = 931 new PixelInterleavedSampleModel(DataBuffer.TYPE_INT, 932 tileWidth, 933 tileHeight, 934 nComp, 935 tileWidth * nComp, 936 bandOffsets); 937 else 938 throw new IllegalArgumentException(I18N.getString("J2KReadState11") + " " + 939 + maxDepth); 940 return sampleModel; 941 } 942 943 public ColorModel getColorModel() { 944 945 if (colorModel != null) 946 return colorModel; 947 948 // Attempt to get the ColorModel from the JP2 boxes. 949 colorModel = ff.getColorModel(); 950 if (colorModel != null) 951 return colorModel; 952 953 if(hi.siz.csiz <= 4) { 954 // XXX: Code essentially duplicated from FileFormatReader.getColorModel(). 955 // Create the ColorModel from the SIZ marker segment parameters. 956 ColorSpace cs; 957 if(hi.siz.csiz > 2) { 958 cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 959 } else { 960 cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); 961 } 962 963 int[] bitsPerComponent = new int[hi.siz.csiz]; 964 boolean isSigned = false; 965 int maxBitDepth = -1; 966 for(int i = 0; i < hi.siz.csiz; i++) { 967 bitsPerComponent[i] = hi.siz.getOrigBitDepth(i); 968 if(maxBitDepth < bitsPerComponent[i]) { 969 maxBitDepth = bitsPerComponent[i]; 970 } 971 isSigned |= hi.siz.isOrigSigned(i); 972 } 973 974 boolean hasAlpha = hi.siz.csiz % 2 == 0; 975 976 int type = -1; 977 978 if (maxBitDepth <= 8) { 979 type = DataBuffer.TYPE_BYTE; 980 } else if (maxBitDepth <= 16) { 981 type = isSigned ? DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT; 982 } else if (maxBitDepth <= 32) { 983 type = DataBuffer.TYPE_INT; 984 } 985 986 if (type != -1) { 987 if(hi.siz.csiz == 1 && 988 (maxBitDepth == 1 || maxBitDepth == 2 || maxBitDepth == 4)) { 989 colorModel = ImageUtil.createColorModel(getSampleModel()); 990 } else { 991 colorModel = new ComponentColorModel(cs, 992 bitsPerComponent, 993 hasAlpha, 994 false, 995 hasAlpha ? 996 Transparency.TRANSLUCENT : 997 Transparency.OPAQUE , 998 type); 999 } 1000 1001 return colorModel; 1002 } 1003 } 1004 1005 if(sampleModel == null) { 1006 sampleModel = getSampleModel(); 1007 } 1008 1009 if (sampleModel == null) 1010 return null; 1011 1012 return ImageUtil.createColorModel(null, sampleModel); 1013 } 1014 1015 /** 1016 * Returns the bounding rectangle of the upper left tile at 1017 * the current resolution level. 1018 */ 1019 Rectangle getTile0Rect() { 1020 return new Rectangle(tileXOffset, tileYOffset, tileWidth, tileHeight); 1021 } 1022 1023 private int clip(int value, int min, int max) { 1024 if (value < min) 1025 value = min; 1026 if (value > max) 1027 value = max; 1028 return value; 1029 } 1030 1031 private void clipDestination(Rectangle dest) { 1032 Point offset = j2krparam.getDestinationOffset(); 1033 if (dest.x < offset.x) { 1034 dest.width += dest.x - offset.x; 1035 dest.x = offset.x ; 1036 } 1037 if (dest.y < offset.y) { 1038 dest.height += dest.y - offset.y; 1039 dest.y = offset.y ; 1040 } 1041 } 1042}