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