001/* 002 * $RCSfile: ROIScaler.java,v $ 003 * $Revision: 1.1 $ 004 * $Date: 2005/02/11 05:02:23 $ 005 * $State: Exp $ 006 * 007 * Class: ROIScaler 008 * 009 * Description: This class takes care of the scaling of the 010 * samples 011 * 012 * 013 * 014 * COPYRIGHT: 015 * 016 * This software module was originally developed by Raphaël Grosbois and 017 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel 018 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David 019 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research 020 * Centre France S.A) in the course of development of the JPEG2000 021 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This 022 * software module is an implementation of a part of the JPEG 2000 023 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio 024 * Systems AB and Canon Research Centre France S.A (collectively JJ2000 025 * Partners) agree not to assert against ISO/IEC and users of the JPEG 026 * 2000 Standard (Users) any of their rights under the copyright, not 027 * including other intellectual property rights, for this software module 028 * with respect to the usage by ISO/IEC and Users of this software module 029 * or modifications thereof for use in hardware or software products 030 * claiming conformance to the JPEG 2000 Standard. Those intending to use 031 * this software module in hardware or software products are advised that 032 * their use may infringe existing patents. The original developers of 033 * this software module, JJ2000 Partners and ISO/IEC assume no liability 034 * for use of this software module or modifications thereof. No license 035 * or right to this software module is granted for non JPEG 2000 Standard 036 * conforming products. JJ2000 Partners have full right to use this 037 * software module for his/her own purpose, assign or donate this 038 * software module to any third party and to inhibit third parties from 039 * using this software module for non JPEG 2000 Standard conforming 040 * products. This copyright notice must be included in all copies or 041 * derivative works of this software module. 042 * 043 * Copyright (c) 1999/2000 JJ2000 Partners. 044 * */ 045package jj2000.j2k.roi.encoder; 046import java.awt.Point; 047 048import jj2000.j2k.quantization.quantizer.*; 049import jj2000.j2k.codestream.writer.*; 050import jj2000.j2k.wavelet.analysis.*; 051import jj2000.j2k.quantization.*; 052import jj2000.j2k.image.input.*; 053import jj2000.j2k.wavelet.*; 054import jj2000.j2k.image.*; 055import jj2000.j2k.util.*; 056import jj2000.j2k.roi.*; 057import jj2000.j2k.*; 058 059import java.util.*; 060import java.io.*; 061 062import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava; 063/** 064 * This class deals with the ROI functionality. 065 * 066 * <p>The ROI method is the Maxshift method. The ROIScaler works by scaling 067 * the quantized wavelet coefficients that do not affect the ROI (i.e 068 * background coefficients) so that these samples get a lower significance 069 * than the ROI ones. By scaling the coefficients sufficiently, the ROI 070 * coefficients can be recognized by their amplitude alone and no ROI mask 071 * needs to be generated at the decoder side. 072 * 073 * <p>The source module must be a quantizer and code-block's data is exchange 074 * with thanks to CBlkWTData instances. 075 * 076 * @see Quantizer 077 * @see CBlkWTData 078 * */ 079public class ROIScaler extends ImgDataAdapter implements CBlkQuantDataSrcEnc { 080 081 /** The prefix for ROI Scaler options: 'R' */ 082 public final static char OPT_PREFIX = 'R'; 083 084 /** The list of parameters that are accepted for ROI coding. Options 085 * for ROI Scaler start with 'R'. */ 086 private final static String [][] pinfo = { 087 { "Rroi","[<component idx>] R <left> <top> <width> <height>"+ 088 " or [<component idx>] C <centre column> <centre row> "+ 089 "<radius> or [<component idx>] A <filename>", 090 "Specifies ROIs shape and location. The shape can be either "+ 091 "rectangular 'R', or circular 'C' or arbitrary 'A'. "+ 092 "Each new occurrence of an 'R', a 'C' or an 'A' is a new ROI. "+ 093 "For circular and rectangular ROIs, all values are "+ 094 "given as their pixel values relative to the canvas origin. "+ 095 "Arbitrary shapes must be included in a PGM file where non 0 "+ 096 "values correspond to ROI coefficients. The PGM file must have "+ 097 "the size as the image. "+ 098 "The component idx specifies which components "+ 099 "contain the ROI. The component index is specified as described "+ 100 "by points 3 and 4 in the general comment on tile-component idx. "+ 101 "If this option is used, the codestream is layer progressive by "+ 102 "default unless it is overridden by the 'Aptype' option.", 103 null}, 104 { "Ralign","[true|false]", 105 "By specifying this argument, the ROI mask will be "+ 106 "limited to covering only entire code-blocks. The ROI coding can "+ 107 "then be performed without any actual scaling of the coefficients "+ 108 "but by instead scaling the distortion estimates.","false"}, 109 { "Rstart_level","<level>", 110 "This argument forces the lowest <level> resolution levels to "+ 111 "belong to the ROI. By doing this, it is possible to avoid only "+ 112 "getting information for the ROI at an early stage of "+ 113 "transmission.<level> = 0 means the lowest resolution level "+ 114 "belongs to the ROI, 1 means the two lowest etc. (-1 deactivates"+ 115 " the option)","-1"}, 116 { "Rno_rect","[true|false]", 117 "This argument makes sure that the ROI mask generation is not done "+ 118 "using the fast ROI mask generation for rectangular ROIs "+ 119 "regardless of whether the specified ROIs are rectangular or not", 120 "false"}, 121 }; 122 123 /** The maximum number of magnitude bit-planes in any subband. One value 124 * for each tile-component */ 125 private int maxMagBits[][]; 126 127 /** Flag indicating the presence of ROIs */ 128 private boolean roi; 129 130 /** Flag indicating if block aligned ROIs are used */ 131 private boolean blockAligned; 132 133 /** Number of resolution levels to include in ROI mask */ 134 private int useStartLevel; 135 136 /** The class generating the ROI mask */ 137 private ROIMaskGenerator mg; 138 139 /** The ROI mask */ 140 private DataBlkInt roiMask; 141 142 /** The source of quantized wavelet transform coefficients */ 143 private Quantizer src; 144 145 /** 146 * Constructor of the ROI scaler, takes a Quantizer as source of data to 147 * scale. 148 * 149 * @param src The quantizer that is the source of data. 150 * 151 * @param mg The mask generator that will be used for all components 152 * 153 * @param roi Flag indicating whether there are rois specified. 154 * 155 * @param sLev The resolution levels that belong entirely to ROI 156 * 157 * @param uba Flag indicating whether block aligning is used. 158 * 159 * @param encSpec The encoder specifications for addition of roi specs 160 * */ 161 public ROIScaler(Quantizer src, 162 ROIMaskGenerator mg, 163 boolean roi, 164 int sLev, 165 boolean uba, 166 J2KImageWriteParamJava wp){ 167 super(src); 168 this.src = src; 169 this.roi = roi; 170 this.useStartLevel = sLev; 171 if(roi){ 172 // If there is no ROI, no need to do this 173 this.mg = mg; 174 roiMask = new DataBlkInt(); 175 calcMaxMagBits(wp); 176 blockAligned = uba; 177 } 178 } 179 180 /** 181 * Since ROI scaling is always a reversible operation, it calls 182 * isReversible() method of it source (the quantizer module). 183 * 184 * @param t The tile to test for reversibility 185 * 186 * @param c The component to test for reversibility 187 * 188 * @return True if the quantized data is reversible, false if not. 189 * */ 190 public boolean isReversible(int t,int c){ 191 return src.isReversible(t,c); 192 } 193 194 /** 195 * Returns a reference to the subband tree structure representing the 196 * subband decomposition for the specified tile-component. 197 * 198 * @param t The index of the tile. 199 * 200 * @param c The index of the component. 201 * 202 * @return The subband tree structure, see SubbandAn. 203 * 204 * @see SubbandAn 205 * 206 * @see Subband 207 * */ 208 public SubbandAn getAnSubbandTree(int t,int c) { 209 return src.getAnSubbandTree(t,c); 210 } 211 212 /** 213 * Returns the horizontal offset of the code-block partition. Allowable 214 * values are 0 and 1, nothing else. 215 * */ 216 public int getCbULX() { 217 return src.getCbULX(); 218 } 219 220 /** 221 * Returns the vertical offset of the code-block partition. Allowable 222 * values are 0 and 1, nothing else. 223 * */ 224 public int getCbULY() { 225 return src.getCbULY(); 226 } 227 228 /** 229 * Creates a ROIScaler object. The Quantizer is the source of data to 230 * scale. 231 * 232 * <P> The ROI Scaler creates a ROIMaskGenerator depending on what ROI 233 * information is in the J2KImageWriteParamJava. If only rectangular ROI are used, 234 * the fast mask generator for rectangular ROI can be used. 235 * 236 * @param src The source of data to scale 237 * 238 * @param pl The parameter list (or options). 239 * 240 * @param encSpec The encoder specifications for addition of roi specs 241 * 242 * @exception IllegalArgumentException If an error occurs while parsing 243 * the options in 'pl' 244 * */ 245 public static ROIScaler createInstance(Quantizer src, 246 J2KImageWriteParamJava wp){ 247 Vector roiVector = new Vector(); 248 ROIMaskGenerator maskGen = null; 249 250 /* XXX: need investigation 251 // Check parameters 252 pl.checkList(OPT_PREFIX,pl.toNameArray(pinfo)); 253 */ 254 255 // Get parameters and check if there are and ROIs specified 256 String roiopt = wp.getROIs().getSpecified(); 257 if (roiopt == null) { 258 // No ROIs specified! Create ROIScaler with no mask generator 259 return new ROIScaler(src,null,false,-1,false,wp); 260 } 261 262 // Check if the lowest resolution levels should belong to the ROI 263 int sLev = wp.getStartLevelROI(); 264 265 // Check if the ROIs are block-aligned 266 boolean useBlockAligned = wp.getAlignROI(); 267 268 // Check if generic mask generation is specified 269 boolean onlyRect = false; 270 271 // Parse the ROIs 272 parseROIs(roiopt,src.getNumComps(),roiVector); 273 ROI[] roiArray = new ROI[roiVector.size()]; 274 roiVector.copyInto(roiArray); 275 276 // If onlyRect has been forced, check if there are any non-rectangular 277 // ROIs specified. Currently, only the presence of circular ROIs will 278 // make this false 279 if(onlyRect) { 280 for(int i=roiArray.length-1 ; i>=0 ; i--) 281 if(!roiArray[i].rect){ 282 onlyRect=false; 283 break; 284 } 285 } 286 287 if(onlyRect){ 288 // It's possible to use the fast ROI mask generation when only 289 // rectangular ROIs are specified. 290 maskGen = new RectROIMaskGenerator(roiArray,src.getNumComps()); 291 } 292 else{ 293 // It's necessary to use the generic mask generation 294 maskGen = new ArbROIMaskGenerator(roiArray,src.getNumComps(),src); 295 } 296 297 return new ROIScaler(src,maskGen,true,sLev,useBlockAligned,wp); 298 } 299 300 /** 301 * This function parses the values given for the ROIs with the argument 302 * -Rroi. Currently only circular and rectangular ROIs are supported. 303 * 304 * <P> A rectangular ROI is indicated by a 'R' followed the coordinates 305 * for the upper left corner of the ROI and then its width and height. 306 * 307 * <P> A circular ROI is indicated by a 'C' followed by the coordinates of 308 * the circle center and then the radius. 309 * 310 * <P> Before the R and C values, the component that are affected by the 311 * ROI are indicated. 312 * 313 * @param roiopt The info on the ROIs 314 * 315 * @param nc number of components 316 * 317 * @param roiVector The vcector containing the ROI parsed from the cmd line 318 * 319 * @return The ROIs specified in roiopt 320 * */ 321 protected static Vector parseROIs(String roiopt, int nc,Vector roiVector){ 322 ROI[] ROIs; 323 ROI roi; 324 StringTokenizer stok; 325 char tok; 326 int nrOfROIs = 0; 327 char c; 328 int comp,ulx,uly,w,h,x,y,rad; 329 boolean[] roiInComp = null; 330 331 stok = new StringTokenizer(roiopt); 332 333 String word; 334 while(stok.hasMoreTokens()){ 335 word = stok.nextToken(); 336 337 switch(word.charAt(0)){ 338 case 'c': // Components specification 339 roiInComp = ModuleSpec.parseIdx(word,nc); 340 break; 341 case 'R': // Rectangular ROI to be read 342 nrOfROIs++; 343 try{ 344 word = stok.nextToken(); 345 ulx = (new Integer(word)).intValue(); 346 word = stok.nextToken(); 347 uly = (new Integer(word)).intValue(); 348 word = stok.nextToken(); 349 w = (new Integer(word)).intValue(); 350 word = stok.nextToken(); 351 h = (new Integer(word)).intValue(); 352 } 353 catch(NumberFormatException e){ 354 throw new IllegalArgumentException("Bad parameter for "+ 355 "'-Rroi R' option : "+ 356 word); 357 } 358 catch(NoSuchElementException f){ 359 throw new IllegalArgumentException("Wrong number of "+ 360 "parameters for "+ 361 "h'-Rroi R' option."); 362 } 363 364 // If the ROI is component-specific, check which comps. 365 if(roiInComp != null) 366 for(int i=0; i<nc;i++){ 367 if(roiInComp[i]){ 368 roi=new ROI(i,ulx,uly,w,h); 369 roiVector.addElement(roi); 370 } 371 } 372 else{ // Otherwise add ROI for all components 373 for(int i=0; i<nc;i++){ 374 roi=new ROI(i,ulx,uly,w,h); 375 roiVector.addElement(roi); 376 } 377 } 378 break; 379 case 'C': // Circular ROI to be read 380 nrOfROIs++; 381 382 try{ 383 word = stok.nextToken(); 384 x = (new Integer(word)).intValue(); 385 word = stok.nextToken(); 386 y = (new Integer(word)).intValue(); 387 word = stok.nextToken(); 388 rad = (new Integer(word)).intValue(); 389 } 390 catch(NumberFormatException e){ 391 throw new IllegalArgumentException("Bad parameter for "+ 392 "'-Rroi C' option : "+ 393 word); 394 } 395 catch(NoSuchElementException f){ 396 throw new IllegalArgumentException("Wrong number of "+ 397 "parameters for "+ 398 "'-Rroi C' option."); 399 } 400 401 // If the ROI is component-specific, check which comps. 402 if(roiInComp != null) 403 for(int i=0; i<nc; i++){ 404 if(roiInComp[i]){ 405 roi = new ROI(i,x,y,rad); 406 roiVector.addElement(roi); 407 } 408 } 409 else{ // Otherwise add ROI for all components 410 for(int i=0; i<nc; i++){ 411 roi = new ROI(i,x,y,rad); 412 roiVector.addElement(roi); 413 } 414 } 415 break; 416 case 'A': // ROI wth arbitrary shape 417 nrOfROIs++; 418 419 String filename; 420 ImgReaderPGM maskPGM = null; 421 422 try{ 423 filename = stok.nextToken(); 424 } 425 catch(NoSuchElementException e){ 426 throw new IllegalArgumentException("Wrong number of "+ 427 "parameters for "+ 428 "'-Rroi A' option."); 429 } 430 try { 431 maskPGM = new ImgReaderPGM(filename); 432 } 433 catch(IOException e){ 434 throw new Error("Cannot read PGM file with ROI"); 435 } 436 437 // If the ROI is component-specific, check which comps. 438 if(roiInComp != null) 439 for(int i=0; i<nc; i++){ 440 if(roiInComp[i]){ 441 roi = new ROI(i,maskPGM); 442 roiVector.addElement(roi); 443 } 444 } 445 else{ // Otherwise add ROI for all components 446 for(int i=0; i<nc; i++){ 447 roi = new ROI(i,maskPGM); 448 roiVector.addElement(roi); 449 } 450 } 451 break; 452 default: 453 throw new Error("Bad parameters for ROI nr "+roiVector.size()); 454 } 455 } 456 457 return roiVector; 458 } 459 460 /** 461 * This function gets a datablk from the entropy coder. The sample sin the 462 * block, which consists of the quantized coefficients from the quantizer, 463 * are scaled by the values given for any ROIs specified. 464 * 465 * <P>The function calls on a ROIMaskGenerator to get the mask for 466 * scaling the coefficients in the current block. 467 * 468 * <P>The data returned by this method is a copy of the orignal 469 * data. Therfore it can be modified "in place" without any problems after 470 * being returned. The 'offset' of the returned data is 0, and the 'scanw' 471 * is the same as the code-block width. See the 'CBlkWTData' class. 472 * 473 * @param n The component for which to return the next code-block. 474 * 475 * @param cblk If non-null this object will be used to return the new 476 * code-block. If null a new one will be allocated and returned. If the 477 * "data" array of the object is non-null it will be reused, if possible, 478 * to return the data. 479 * 480 * @return The next code-block in the current tile for component 'n', or 481 * null if all code-blocks for the current tile have been returned. 482 * 483 * @see CBlkWTData 484 * */ 485 public CBlkWTData getNextCodeBlock(int n, CBlkWTData cblk) { 486 return getNextInternCodeBlock(n,cblk); 487 } 488 489 /** 490 * This function gets a datablk from the entropy coder. The sample sin the 491 * block, which consists of the quantized coefficients from the quantizer, 492 * are scaled by the values given for any ROIs specified. 493 * 494 * <P>The function calls on a ROIMaskGenerator to get the mask for 495 * scaling the coefficients in the current block. 496 * 497 * @param c The component for which to return the next code-block. 498 * 499 * @param cblk If non-null this object will be used to return the new 500 * code-block. If null a new one will be allocated and returned. If the 501 * "data" array of the object is non-null it will be reused, if possible, 502 * to return the data. 503 * 504 * @return The next code-block in the current tile for component 'n', or 505 * null if all code-blocks for the current tile have been returned. 506 * 507 * @see CBlkWTData 508 * */ 509 public CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk){ 510 int mi,i,j,k,wrap; 511 int ulx, uly, w, h; 512 DataBlkInt mask = roiMask; // local copy of mask 513 int[] maskData; // local copy of mask data 514 int[] data; // local copy of quantized data 515 int tmp; 516 int bitMask = 0x7FFFFFFF; 517 SubbandAn root,sb; 518 int maxBits = 0; // local copy 519 boolean roiInTile; 520 boolean sbInMask; 521 int nROIcoeff = 0; 522 523 // Get codeblock's data from quantizer 524 cblk = src.getNextCodeBlock(c,cblk); 525 526 // If there is no ROI in the image, or if we already got all 527 // code-blocks 528 if (!roi || cblk == null) { 529 return cblk; 530 } 531 532 data = (int[])cblk.getData(); 533 sb = cblk.sb; 534 ulx = cblk.ulx; 535 uly = cblk.uly; 536 w = cblk.w; 537 h = cblk.h; 538 sbInMask= (sb.resLvl<=useStartLevel); 539 540 // Check that there is an array for the mask and set it to zero 541 maskData = mask.getDataInt(); // local copy of mask data 542 if(maskData==null || w*h>maskData.length){ 543 maskData = new int[w*h]; 544 mask.setDataInt(maskData); 545 } 546 else{ 547 for(i=w*h-1;i>=0;i--) 548 maskData[i]=0; 549 } 550 mask.ulx = ulx; 551 mask.uly = uly; 552 mask.w = w; 553 mask.h = h; 554 555 // Get ROI mask from generator 556 root = src.getAnSubbandTree(tIdx,c); 557 maxBits = maxMagBits[tIdx][c]; 558 roiInTile = mg.getROIMask(mask,root,maxBits,c); 559 560 // If there is no ROI in this tile, return the code-block untouched 561 if(!roiInTile && (!sbInMask)) { 562 cblk.nROIbp = 0; 563 return cblk; 564 } 565 566 // Update field containing the number of ROI magnitude bit-planes 567 cblk.nROIbp = cblk.magbits; 568 569 // If the ROI should adhere to the code-block's boundaries or if the 570 // entire subband belongs to the ROI mask, The code-block is set to 571 // belong entirely to the ROI with the highest scaling value 572 if(sbInMask) { 573 // Scale the wmse so that instead of scaling the coefficients, the 574 // wmse is scaled. 575 cblk.wmseScaling *= (float)(1<<(maxBits<<1)); 576 cblk.nROIcoeff = w*h; 577 return cblk; 578 } 579 580 // In 'block aligned' mode, the code-block is set to belong entirely 581 // to the ROI with the highest scaling value if one coefficient, at 582 // least, belongs to the ROI 583 if(blockAligned) { 584 wrap=cblk.scanw-w; 585 mi=h*w-1; 586 i=cblk.offset+cblk.scanw*(h-1)+w-1; 587 int nroicoeff = 0; 588 for(j=h;j>0;j--){ 589 for(k=w-1;k>=0;k--,i--,mi--){ 590 if (maskData[mi] != 0) { 591 nroicoeff++; 592 } 593 } 594 i -= wrap; 595 } 596 if(nroicoeff!=0) { // Include the subband 597 cblk.wmseScaling *= (float)(1<<(maxBits<<1)); 598 cblk.nROIcoeff = w*h; 599 } 600 return cblk; 601 } 602 603 // Scale background coefficients 604 bitMask=(((1<<cblk.magbits)-1)<<(31-cblk.magbits)); 605 wrap=cblk.scanw-w; 606 mi=h*w-1; 607 i=cblk.offset+cblk.scanw*(h-1)+w-1; 608 for(j=h;j>0;j--){ 609 for(k=w;k>0;k--,i--,mi--){ 610 tmp=data[i]; 611 if (maskData[mi] != 0) { 612 // ROI coeff. We need to erase fractional bits to ensure 613 // that they do not conflict with BG coeffs. This is only 614 // strictly necessary for ROI coeffs. which non-fractional 615 // magnitude is zero, but much better BG quality can be 616 // achieved if done if reset to zero since coding zeros is 617 // much more efficient (the entropy coder knows nothing 618 // about ROI and cannot avoid coding the ROI fractional 619 // bits, otherwise this would not be necessary). 620 data[i] = (0x80000000 & tmp) | (tmp & bitMask); 621 nROIcoeff++; 622 } 623 else { 624 // BG coeff. it is not necessary to erase fractional bits 625 data[i] = (0x80000000 & tmp) | 626 ((tmp & 0x7FFFFFFF) >> maxBits); 627 } 628 } 629 i-=wrap; 630 } 631 632 // Modify the number of significant bit-planes in the code-block 633 cblk.magbits += maxBits; 634 635 // Store the number of ROI coefficients present in the code-block 636 cblk.nROIcoeff = nROIcoeff; 637 638 return cblk; 639 } 640 641 /** 642 * This function returns the ROI mask generator. 643 * 644 * @return The roi mask generator 645 */ 646 public ROIMaskGenerator getROIMaskGenerator(){ 647 return mg; 648 } 649 650 /** 651 * This function returns the blockAligned flag 652 * 653 * @return Flag indicating whether the ROIs were block aligned 654 */ 655 public boolean getBlockAligned(){ 656 return blockAligned; 657 } 658 659 /** 660 * This function returns the flag indicating if any ROI functionality used 661 * 662 * @return Flag indicating whether there are ROIs in the image 663 */ 664 public boolean useRoi(){ 665 return roi; 666 } 667 668 /** 669 * Returns the parameters that are used in this class and 670 * implementing classes. It returns a 2D String array. Each of the 671 * 1D arrays is for a different option, and they have 3 672 * elements. The first element is the option name, the second one 673 * is the synopsis, the third one is a long description of what 674 * the parameter is and the fourth is its default value. The 675 * synopsis or description may be 'null', in which case it is 676 * assumed that there is no synopsis or description of the option, 677 * respectively. Null may be returned if no options are supported. 678 * 679 * @return the options name, their synopsis and their explanation, 680 * or null if no options are supported. 681 * */ 682 public static String[][] getParameterInfo() { 683 return pinfo; 684 } 685 686 /** 687 * Changes the current tile, given the new indexes. An 688 * IllegalArgumentException is thrown if the indexes do not 689 * correspond to a valid tile. 690 * 691 * @param x The horizontal index of the tile. 692 * 693 * @param y The vertical index of the new tile. 694 * */ 695 public void setTile(int x, int y) { 696 super.setTile(x,y); 697 if(roi) 698 mg.tileChanged(); 699 } 700 701 /** 702 * Advances to the next tile, in standard scan-line order (by rows then 703 * columns). An NoNextElementException is thrown if the current tile is 704 * the last one (i.e. there is no next tile). 705 * */ 706 public void nextTile() { 707 super.nextTile(); 708 if(roi) 709 mg.tileChanged(); 710 } 711 712 /** 713 * Calculates the maximum amount of magnitude bits for each 714 * tile-component, and stores it in the 'maxMagBits' array. This is called 715 * by the constructor 716 * 717 * @param encSpec The encoder specifications for addition of roi specs 718 * */ 719 private void calcMaxMagBits(J2KImageWriteParamJava wp) { 720 int tmp; 721 MaxShiftSpec rois = wp.getROIs(); 722 723 int nt = src.getNumTiles(); 724 int nc = src.getNumComps(); 725 726 maxMagBits = new int[nt][nc]; 727 728 src.setTile(0,0); 729 for (int t=0; t<nt; t++) { 730 for (int c=nc-1; c>=0; c--) { 731 tmp = src.getMaxMagBits(c); 732 maxMagBits[t][c] = tmp; 733 rois.setTileCompVal(t,c,new Integer(tmp)); 734 } 735 if( t<nt-1 ) src.nextTile(); 736 } 737 // Reset to current initial tile position 738 src.setTile(0,0); 739 } 740} 741