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