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 sampleModel = getSampleModel(); 382 raster = Raster.createWritableRaster( 383 sampleModel.createCompatibleSampleModel(destinationRegion.x + 384 destinationRegion.width, 385 destinationRegion.y + 386 destinationRegion.height), 387 new Point(0, 0)); 388 } else 389 raster = image.getWritableTile(0, 0); 390 391 readSubsampledRaster(raster); 392 return raster; 393 } 394 395 private void initializeRead(int imageIndex, J2KImageReadParamJava param, 396 J2KMetadata metadata) { 397 try { 398 iis.mark(); 399 in = new IISRandomAccessIO(iis); 400 401 // **** File Format **** 402 // If the codestream is wrapped in the jp2 fileformat, Read the 403 // file format wrapper 404 ff = new FileFormatReader(in, metadata); 405 ff.readFileFormat(); 406 in.seek(ff.getFirstCodeStreamPos()); 407 408 hi = new HeaderInfo(); 409 try{ 410 hd = new HeaderDecoder(in, j2krparam, hi); 411 } catch(EOFException e){ 412 throw new RuntimeException(I18N.getString("J2KReadState2")); 413 } catch (IOException ioe) { 414 throw new RuntimeException(ioe); 415 } 416 417 this.width = hd.getImgWidth(); 418 this.height = hd.getImgHeight(); 419 420 Rectangle sourceRegion = param.getSourceRegion(); 421 sourceOrigin = new Point(); 422 sourceRegion = 423 new Rectangle(hd.getImgULX(), hd.getImgULY(), 424 this.width, this.height); 425 426 // if the subsample rate for components are not consistent 427 boolean compConsistent = true; 428 stepX = hd.getCompSubsX(0); 429 stepY = hd.getCompSubsY(0); 430 for (int i = 1; i < nComp; i++) { 431 if (stepX != hd.getCompSubsX(i) || stepY != hd.getCompSubsY(i)) 432 throw new RuntimeException(I18N.getString("J2KReadState12")); 433 } 434 435 // Get minimum number of resolution levels available across 436 // all tile-components. 437 int minResLevels = hd.getDecoderSpecs().dls.getMin(); 438 439 // Set current resolution level. 440 this.resolution = param != null ? 441 param.getResolution() : minResLevels; 442 if(resolution < 0 || resolution > minResLevels) { 443 resolution = minResLevels; 444 } 445 446 // Convert source region to lower resolution level. 447 if(resolution != minResLevels || stepX != 1 || stepY != 1) { 448 sourceRegion = 449 J2KImageReader.getReducedRect(sourceRegion, minResLevels, 450 resolution, stepX, stepY); 451 } 452 453 destinationRegion = (Rectangle)sourceRegion.clone(); 454 455 J2KImageReader.computeRegionsWrapper(param, 456 false, 457 this.width, 458 this.height, 459 param.getDestination(), 460 sourceRegion, 461 destinationRegion); 462 463 sourceOrigin = new Point(sourceRegion.x, sourceRegion.y); 464 scaleX = param.getSourceXSubsampling(); 465 scaleY = param.getSourceYSubsampling(); 466 xOffset = param.getSubsamplingXOffset(); 467 yOffset = param.getSubsamplingYOffset(); 468 469 this.width = destinationRegion.width; 470 this.height = destinationRegion.height; 471 472 Point tileOffset = hd.getTilingOrigin(null); 473 474 this.tileWidth = hd.getNomTileWidth(); 475 this.tileHeight = hd.getNomTileHeight(); 476 477 // Convert tile 0 to lower resolution level. 478 if(resolution != minResLevels || stepX != 1 || stepY != 1) { 479 Rectangle tileRect = new Rectangle(tileOffset); 480 tileRect.width = tileWidth; 481 tileRect.height = tileHeight; 482 tileRect = 483 J2KImageReader.getReducedRect(tileRect, minResLevels, 484 resolution, stepX, stepY); 485 tileOffset = tileRect.getLocation(); 486 tileWidth = tileRect.width; 487 tileHeight = tileRect.height; 488 } 489 490 tileXOffset = tileOffset.x; 491 tileYOffset = tileOffset.y; 492 493 494 // Set the tile step sizes. These values are used because it 495 // is possible that tiles will be empty. In particular at lower 496 // resolution levels when subsampling is used this may be the 497 // case. This method of calculation will work at least for 498 // Profile-0 images. 499 if(tileWidth*(1 << (minResLevels - resolution))*stepX > 500 hd.getNomTileWidth()) { 501 tileStepX = 502 (tileWidth*(1 << (minResLevels - resolution))*stepX + 503 hd.getNomTileWidth() - 1)/hd.getNomTileWidth(); 504 } else { 505 tileStepX = 1; 506 } 507 508 if(tileHeight*(1 << (minResLevels - resolution))*stepY > 509 hd.getNomTileHeight()) { 510 tileStepY = 511 (tileHeight*(1 << (minResLevels - resolution))*stepY + 512 hd.getNomTileHeight() - 1)/hd.getNomTileHeight(); 513 } else { 514 tileStepY = 1; 515 } 516 517 if (!destinationRegion.equals(sourceRegion)) 518 noTransform = false; 519 520 // **** Header decoder **** 521 // Instantiate header decoder and read main header 522 decSpec = hd.getDecoderSpecs(); 523 524 // **** Instantiate decoding chain **** 525 // Get demixed bitdepths 526 nComp = hd.getNumComps(); 527 528 int[] depth = new int[nComp]; 529 for (int i=0; i<nComp;i++) 530 depth[i] = hd.getOriginalBitDepth(i); 531 532 //Get channel mapping 533 ChannelDefinitionBox cdb = null; 534 if (metadata != null) 535 cdb = (ChannelDefinitionBox)metadata.getElement("JPEG2000ChannelDefinitionBox"); 536 537 channelMap = new int[nComp]; 538 if (cdb != null && 539 metadata.getElement("JPEG2000PaletteBox") == null) { 540 short[] assoc = cdb.getAssociation(); 541 short[] types = cdb.getTypes(); 542 short[] channels = cdb.getChannel(); 543 544 for (int i = 0; i < types.length; i++) 545 if (types[i] == 0) 546 channelMap[channels[i]] = assoc[i] - 1; 547 else if (types[i] == 1 || types[i] == 2) 548 channelMap[channels[i]] = channels[i]; 549 } else { 550 for (int i = 0; i < nComp; i++) 551 channelMap[i] = i; 552 } 553 554 // **** Bitstream reader **** 555 try { 556 boolean logJJ2000Messages = 557 Boolean.getBoolean("jj2000.j2k.decoder.log"); 558 breader = 559 BitstreamReaderAgent.createInstance(in, hd, 560 j2krparam, decSpec, 561 logJJ2000Messages, hi); 562 } catch (IOException e) { 563 throw new RuntimeException(I18N.getString("J2KReadState3") + " " + 564 ((e.getMessage() != null) ? 565 (":\n"+e.getMessage()) : "")); 566 } catch (IllegalArgumentException e) { 567 throw new RuntimeException(I18N.getString("J2KReadState4") + " " + 568 ((e.getMessage() != null) ? 569 (":\n"+e.getMessage()) : "")); 570 } 571 572 // **** Entropy decoder **** 573 try { 574 entdec = hd.createEntropyDecoder(breader, j2krparam); 575 } catch (IllegalArgumentException e) { 576 throw new RuntimeException(I18N.getString("J2KReadState5") + " " + 577 ((e.getMessage() != null) ? 578 (":\n"+e.getMessage()) : "")); 579 } 580 581 // **** ROI de-scaler **** 582 try { 583 roids = hd.createROIDeScaler(entdec, j2krparam, decSpec); 584 } catch (IllegalArgumentException e) { 585 throw new RuntimeException(I18N.getString("J2KReadState6") + " " + 586 ((e.getMessage() != null) ? 587 (":\n"+e.getMessage()) : "")); 588 } 589 590 591 // **** Dequantizer **** 592 try { 593 deq = hd.createDequantizer(roids, depth, decSpec); 594 } catch (IllegalArgumentException e) { 595 throw new RuntimeException(I18N.getString("J2KReadState7") + " " + 596 ((e.getMessage() != null) ? 597 (":\n"+e.getMessage()) : "")); 598 } 599 600 // **** Inverse wavelet transform *** 601 try { 602 // full page inverse wavelet transform 603 invWT = InverseWT.createInstance(deq,decSpec); 604 } catch (IllegalArgumentException e) { 605 throw new RuntimeException(I18N.getString("J2KReadState8") + " " + 606 ((e.getMessage() != null) ? 607 (":\n"+e.getMessage()) : "")); 608 } 609 610 int res = breader.getImgRes(); 611 int mrl = decSpec.dls.getMin(); 612 invWT.setImgResLevel(res); 613 614 // **** Data converter **** (after inverse transform module) 615 converter = new ImgDataConverter(invWT,0); 616 617 // **** Inverse component transformation **** 618 ictransf = new InvCompTransf(converter, decSpec, depth); 619 620 // If the destination band is set used it 621 sourceBands = j2krparam.getSourceBands(); 622 623 if (sourceBands == null) { 624 sourceBands = new int[nComp]; 625 for (int i = 0; i < nComp; i++) 626 sourceBands[i] = i; 627 } 628 629 nComp = sourceBands.length; 630 631 destinationBands = j2krparam.getDestinationBands(); 632 if (destinationBands == null) { 633 destinationBands = new int[nComp]; 634 for (int i = 0; i < nComp; i++) 635 destinationBands[i] = i; 636 } 637 638 J2KImageReader.checkReadParamBandSettingsWrapper(param, 639 hd.getNumComps(), 640 destinationBands.length); 641 642 levelShift = new int[nComp]; 643 minValues = new int[nComp]; 644 maxValues = new int[nComp]; 645 fracBits = new int[nComp]; 646 dataBlocks = new DataBlkInt[nComp]; 647 648 depth = new int[nComp]; 649 bandOffsets = new int[nComp]; 650 maxDepth = 0; 651 isSigned = false; 652 for (int i=0; i<nComp;i++) { 653 depth[i] = hd.getOriginalBitDepth(sourceBands[i]); 654 if (depth[i] > maxDepth) 655 maxDepth = depth[i]; 656 dataBlocks[i] = new DataBlkInt(); 657 658 //XXX: may need to change if ChannelDefinition is used to 659 // define the color channels, such as BGR order 660 bandOffsets[i] = i; 661 if (hd.isOriginalSigned(sourceBands[i])) 662 isSigned = true; 663 else { 664 levelShift[i] = 665 1<<(ictransf.getNomRangeBits(sourceBands[i])-1); 666 } 667 668 // Get the number of bits in the image, and decide what the max 669 // value should be, depending on whether it is signed or not 670 int nomRangeBits = ictransf.getNomRangeBits(sourceBands[i]); 671 maxValues[i] = (1 << (isSigned == true ? (nomRangeBits-1) : 672 nomRangeBits)) - 1; 673 minValues[i] = isSigned ? -(maxValues[i]+1) : 0; 674 675 fracBits[i] = ictransf.getFixedPoint(sourceBands[i]); 676 } 677 678 iis.reset(); 679 } catch (IllegalArgumentException e){ 680 throw new RuntimeException(e.getMessage(), e); 681 } catch (Error e) { 682 if(e.getMessage()!=null) 683 throw new RuntimeException(e.getMessage(), e); 684 else { 685 throw new RuntimeException(I18N.getString("J2KReadState9"), e); 686 } 687 } catch (RuntimeException e) { 688 if(e.getMessage()!=null) 689 throw new RuntimeException(I18N.getString("J2KReadState10") + " " + 690 e.getMessage(), e); 691 else { 692 throw new RuntimeException(I18N.getString("J2KReadState10"), e); 693 } 694 } catch (Throwable e) { 695 throw new RuntimeException(I18N.getString("J2KReadState10"), e); 696 } 697 } 698 699 private Raster readSubsampledRaster(WritableRaster raster) throws IOException { 700 if (raster == null) 701 raster = Raster.createWritableRaster( 702 sampleModel.createCompatibleSampleModel(destinationRegion.x + 703 destinationRegion.width, 704 destinationRegion.y + 705 destinationRegion.height), 706 new Point(destinationRegion.x, destinationRegion.y)); 707 708 int pixbuf[] = null; // line buffer for pixel data 709 boolean prog = false; // Flag for progressive data 710 Point nT = ictransf.getNumTiles(null); 711 int numBands = sourceBands.length; 712 713 Rectangle destRect = raster.getBounds().intersection(destinationRegion); 714 715 int offx = destinationRegion.x; 716 int offy = destinationRegion.y; 717 718 int sourceSX = (destRect.x - offx) * scaleX + sourceOrigin.x; 719 int sourceSY = (destRect.y - offy) * scaleY + sourceOrigin.y; 720 int sourceEX = (destRect.width - 1)* scaleX + sourceSX; 721 int sourceEY = (destRect.height - 1) * scaleY + sourceSY; 722 723 int startXTile = (sourceSX - tileXOffset) / tileWidth; 724 int startYTile = (sourceSY - tileYOffset) / tileHeight; 725 int endXTile = (sourceEX - tileXOffset) / tileWidth; 726 int endYTile = (sourceEY - tileYOffset) / tileHeight; 727 728 startXTile = clip(startXTile, 0, nT.x - 1); 729 startYTile = clip(startYTile, 0, nT.y - 1); 730 endXTile = clip(endXTile, 0, nT.x - 1); 731 endYTile = clip(endYTile, 0, nT.y - 1); 732 733 int totalXTiles = endXTile - startXTile + 1; 734 int totalYTiles = endYTile - startYTile + 1; 735 int totalTiles = totalXTiles * totalYTiles; 736 737 // Start the data delivery to the cached consumers tile by tile 738 for(int y=startYTile; y <= endYTile; y++){ 739 if (reader.getAbortRequest()) 740 break; 741 742 // Loop on horizontal tiles 743 for(int x=startXTile; x <= endXTile; x++){ 744 if (reader.getAbortRequest()) 745 break; 746 747 float initialFraction = 748 (x - startXTile + (y - startYTile)*totalXTiles)/totalTiles; 749 750 ictransf.setTile(x*tileStepX,y*tileStepY); 751 752 int sx = hd.getCompSubsX(0); 753 int cTileWidth = (ictransf.getTileWidth() + sx - 1)/sx; 754 int sy = hd.getCompSubsY(0); 755 int cTileHeight = (ictransf.getTileHeight() + sy - 1)/sy; 756 757 // Offsets within the tile. 758 int tx = 0; 759 int ty = 0; 760 761 // The region for this tile 762 int startX = tileXOffset + x * tileWidth; 763 int startY = tileYOffset + y * tileHeight; 764 765 // sourceSX is guaranteed to be >= startX 766 if (sourceSX > startX) { 767 if(startX >= hd.getImgULX()) { 768 tx = sourceSX - startX; // Intra-tile offset. 769 cTileWidth -= tx; // Reduce effective width. 770 } 771 startX = sourceSX; // Absolute position. 772 } 773 774 // sourceSY is guaranteed to be >= startY 775 if (sourceSY > startY) { 776 if(startY >= hd.getImgULY()) { 777 ty = sourceSY - startY; // Intra-tile offset. 778 cTileHeight -= ty; // Reduce effective width. 779 } 780 startY = sourceSY; // Absolute position. 781 } 782 783 // Decrement dimensions if end position is within tile. 784 if (sourceEX < startX + cTileWidth - 1) { 785 cTileWidth += sourceEX - startX - cTileWidth + 1; 786 } 787 if (sourceEY < startY + cTileHeight - 1) { 788 cTileHeight += sourceEY - startY - cTileHeight + 1; 789 } 790 791 // The start X in the destination 792 int x1 = (startX + scaleX - 1 - sourceOrigin.x) / scaleX; 793 int x2 = (startX + scaleX -1 + cTileWidth - sourceOrigin.x) / 794 scaleX; 795 int lineLength = x2 - x1; 796 if (pixbuf == null || pixbuf.length < lineLength) 797 pixbuf = new int[lineLength]; // line buffer for pixel data 798 x2 = (x2 - 1) * scaleX + sourceOrigin.x - startX; 799 800 int y1 = (startY + scaleY -1 - sourceOrigin.y) /scaleY; 801 802 x1 += offx; 803 y1 += offy; 804 805 // check to see if we have YCbCr data 806 boolean ycbcr = false; 807 808 for (int i=0; i<numBands; i++) { 809 DataBlkInt db = dataBlocks[i]; 810 db.ulx = tx; 811 db.uly = ty + cTileHeight - 1; 812 db.w = cTileWidth; 813 db.h = 1; 814 815 try { 816 ictransf.getInternCompData(db, channelMap[sourceBands[i]]); 817 } 818 catch (ArrayIndexOutOfBoundsException e) { 819 ycbcr = true; 820 break; 821 } 822 } 823 824 // Deliver in lines to reduce memory usage 825 for (int l = ty, m = y1; 826 l < ty + cTileHeight; 827 l += scaleY, m++) { 828 if (reader.getAbortRequest()) 829 break; 830 831 832 if (ycbcr) { 833 DataBlkInt lum = dataBlocks[0]; 834 DataBlkInt cb = dataBlocks[1]; 835 DataBlkInt cr = dataBlocks[2]; 836 837 lum.ulx = tx; 838 lum.uly = l; 839 lum.w = cTileWidth; 840 lum.h = 1; 841 ictransf.getInternCompData(lum, channelMap[sourceBands[0]]); 842 prog = prog || lum.progressive; 843 844 cb.ulx = tx; 845 cb.uly = l; 846 cb.w = cTileWidth / 2; 847 cb.h = 1; 848 ictransf.getInternCompData(cb, channelMap[sourceBands[1]]); 849 prog = prog || cb.progressive; 850 851 cr.ulx = tx; 852 cr.uly = l; 853 cr.w = cTileWidth / 2; 854 cr.h = 1; 855 ictransf.getInternCompData(cr, channelMap[sourceBands[2]]); 856 prog = prog || cr.progressive; 857 858 int[] lumdata = lum.data; 859 int[] cbdata = cb.data; 860 int[] crdata = cr.data; 861 862 int k1 = lum.offset + x2; 863 864 int fracBit = fracBits[0]; 865 int lS = levelShift[0]; 866 int min = minValues[0]; 867 int max = maxValues[0]; 868 869 int[][] pix = new int[3][lineLength]; 870 871 for (int j = lineLength - 1; j >= 0; j--, k1-=scaleX) { 872 int red = (lumdata[k1] >> fracBit) + lS; 873 red = (red < min) ? min : ((red > max) ? max : red); 874 875 int cIndex = k1 / 2; 876 877 int chrom1 = cbdata[cIndex]; 878 int chrom2 = crdata[cIndex]; 879 int lumval = red; 880 881 red = (int) (chrom2 * 1.542 + lumval); 882 int blue = (int) (lumval + 1.772 * chrom1 - 0.886); 883 int green = (int) (lumval - 0.34413 * chrom1 - 0.71414 * 884 chrom2 - 0.1228785); 885 886 if (red > 255) red = 255; 887 if (green > 255) green = 255; 888 if (blue > 255) blue = 255; 889 890 if (red < 0) red = 0; 891 if (green < 0) green = 0; 892 if (blue < 0) blue = 0; 893 894 pix[0][j] = red; 895 pix[1][j] = green; 896 pix[2][j] = blue; 897 } 898 899 900 raster.setSamples(x1, m, lineLength, 1, 901 destinationBands[0], pix[0]); 902 raster.setSamples(x1, m, lineLength, 1, 903 destinationBands[1], pix[1]); 904 raster.setSamples(x1, m, lineLength, 1, 905 destinationBands[2], pix[2]); 906 907 continue; 908 } 909 910 // Request line data 911 for (int i = 0; i < numBands; i++) { 912 DataBlkInt db = dataBlocks[i]; 913 db.ulx = tx; 914 db.uly = l; 915 db.w = ycbcr && i > 0 ? cTileWidth / 2 : cTileWidth; 916 db.h = 1; 917 ictransf.getInternCompData(db, channelMap[sourceBands[i]]); 918 prog = prog || db.progressive; 919 920 int[] data = db.data; 921 int k1 = db.offset + x2; 922 923 int fracBit = fracBits[i]; 924 int lS = levelShift[i]; 925 int min = minValues[i]; 926 int max = maxValues[i]; 927 928 if (ImageUtil.isBinary(sampleModel)) { 929 // Force min max to 0 and 1. 930 min = 0; 931 max = 1; 932 if (bytebuf == null || bytebuf.length < cTileWidth * numBands) 933 bytebuf = new byte[cTileWidth * numBands]; 934 for (int j = lineLength - 1; j >= 0; j--, k1-=scaleX) { 935 int tmp = (data[k1] >> fracBit) + lS; 936 bytebuf[j] = 937 (byte)((tmp < min) ? min : 938 ((tmp > max) ? max : tmp)); 939 } 940 941 ImageUtil.setUnpackedBinaryData(bytebuf, 942 raster, 943 new Rectangle(x1, 944 m, 945 lineLength, 946 1)); 947 } else { 948 for (int j = lineLength - 1; j >= 0; j--, k1-=scaleX) { 949 int tmp = (data[k1] >> fracBit) + lS; 950 pixbuf[j] = (tmp < min) ? min : 951 ((tmp > max) ? max : tmp); 952 } 953 954 // Send the line data to the BufferedImage 955 raster.setSamples(x1, 956 m, 957 lineLength, 958 1, 959 destinationBands[i], 960 pixbuf); 961 } 962 } 963 964 if (destImage != null) 965 reader.processImageUpdateWrapper(destImage, x1, m, 966 cTileWidth, 1, 1, 1, 967 destinationBands); 968 969 float fraction = initialFraction + 970 (l - ty + 1.0F)/cTileHeight/totalTiles; 971 reader.processImageProgressWrapper(100.0f*fraction); 972 } 973 } // End loop on horizontal tiles 974 } // End loop on vertical tiles 975 976 return raster; 977 } 978 979 public ImageTypeSpecifier getImageType() 980 throws IOException { 981 982 getSampleModel(); 983 getColorModel(); 984 985 return new ImageTypeSpecifier(colorModel, sampleModel); 986 } 987 988 public SampleModel getSampleModel() { 989 if (sampleModel != null) 990 return sampleModel; 991 992 int realWidth = (int) Math.min(tileWidth, width); 993 int realHeight = (int) Math.min(tileHeight, height); 994 995 if (nComp == 1 && (maxDepth == 1 || maxDepth == 2 || maxDepth == 4)) 996 sampleModel = 997 new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 998 realWidth, 999 realHeight, 1000 maxDepth); 1001 else if (maxDepth <= 8) 1002 sampleModel = 1003 new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 1004 realWidth, 1005 realHeight, 1006 nComp, 1007 realWidth * nComp, 1008 bandOffsets); 1009 else if (maxDepth <=16) 1010 sampleModel = 1011 new PixelInterleavedSampleModel(isSigned ? 1012 DataBuffer.TYPE_SHORT : 1013 DataBuffer.TYPE_USHORT, 1014 realWidth, realHeight, 1015 nComp, 1016 realWidth * nComp, 1017 bandOffsets); 1018 else if (maxDepth <= 32) 1019 sampleModel = 1020 new PixelInterleavedSampleModel(DataBuffer.TYPE_INT, 1021 realWidth, 1022 realHeight, 1023 nComp, 1024 realWidth * nComp, 1025 bandOffsets); 1026 else 1027 throw new IllegalArgumentException(I18N.getString("J2KReadState11") + " " + 1028 + maxDepth); 1029 return sampleModel; 1030 } 1031 1032 public ColorModel getColorModel() { 1033 1034 if (colorModel != null) 1035 return colorModel; 1036 1037 // Attempt to get the ColorModel from the JP2 boxes. 1038 colorModel = ff.getColorModel(); 1039 if (colorModel != null) 1040 return colorModel; 1041 1042 if(hi.siz.csiz <= 4) { 1043 // XXX: Code essentially duplicated from FileFormatReader.getColorModel(). 1044 // Create the ColorModel from the SIZ marker segment parameters. 1045 ColorSpace cs; 1046 if(hi.siz.csiz > 2) { 1047 cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); 1048 } else { 1049 cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); 1050 } 1051 1052 int[] bitsPerComponent = new int[hi.siz.csiz]; 1053 boolean isSigned = false; 1054 int maxBitDepth = -1; 1055 for(int i = 0; i < hi.siz.csiz; i++) { 1056 bitsPerComponent[i] = hi.siz.getOrigBitDepth(i); 1057 if(maxBitDepth < bitsPerComponent[i]) { 1058 maxBitDepth = bitsPerComponent[i]; 1059 } 1060 isSigned |= hi.siz.isOrigSigned(i); 1061 } 1062 1063 boolean hasAlpha = hi.siz.csiz % 2 == 0; 1064 1065 int type = -1; 1066 1067 if (maxBitDepth <= 8) { 1068 type = DataBuffer.TYPE_BYTE; 1069 } else if (maxBitDepth <= 16) { 1070 type = isSigned ? DataBuffer.TYPE_SHORT : DataBuffer.TYPE_USHORT; 1071 } else if (maxBitDepth <= 32) { 1072 type = DataBuffer.TYPE_INT; 1073 } 1074 1075 if (type != -1) { 1076 if(hi.siz.csiz == 1 && 1077 (maxBitDepth == 1 || maxBitDepth == 2 || maxBitDepth == 4)) { 1078 colorModel = ImageUtil.createColorModel(getSampleModel()); 1079 } else { 1080 colorModel = new ComponentColorModel(cs, 1081 bitsPerComponent, 1082 hasAlpha, 1083 false, 1084 hasAlpha ? 1085 Transparency.TRANSLUCENT : 1086 Transparency.OPAQUE , 1087 type); 1088 } 1089 1090 return colorModel; 1091 } 1092 } 1093 1094 if(sampleModel == null) { 1095 sampleModel = getSampleModel(); 1096 } 1097 1098 if (sampleModel == null) 1099 return null; 1100 1101 return ImageUtil.createColorModel(null, sampleModel); 1102 } 1103 1104 /** 1105 * Returns the bounding rectangle of the upper left tile at 1106 * the current resolution level. 1107 */ 1108 Rectangle getTile0Rect() { 1109 return new Rectangle(tileXOffset, tileYOffset, tileWidth, tileHeight); 1110 } 1111 1112 private int clip(int value, int min, int max) { 1113 if (value < min) 1114 value = min; 1115 if (value > max) 1116 value = max; 1117 return value; 1118 } 1119 1120 private void clipDestination(Rectangle dest) { 1121 Point offset = j2krparam.getDestinationOffset(); 1122 if (dest.x < offset.x) { 1123 dest.width += dest.x - offset.x; 1124 dest.x = offset.x ; 1125 } 1126 if (dest.y < offset.y) { 1127 dest.height += dest.y - offset.y; 1128 dest.y = offset.y ; 1129 } 1130 } 1131}