001: /*
002: * $RCSfile: J2KImageReader.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.7 $
042: * $Date: 2006/10/05 01:08:30 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.jpeg2000;
046:
047: import java.awt.Rectangle;
048: import java.awt.Point;
049:
050: import javax.imageio.IIOException;
051: import javax.imageio.ImageReader;
052: import javax.imageio.ImageReadParam;
053: import javax.imageio.ImageTypeSpecifier;
054: import javax.imageio.metadata.IIOMetadata;
055: import javax.imageio.spi.ImageReaderSpi;
056: import javax.imageio.stream.ImageInputStream;
057: import com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam;
058:
059: import java.awt.image.BufferedImage;
060: import java.awt.image.Raster;
061: import java.awt.image.RenderedImage;
062:
063: import java.io.*;
064: import java.util.List;
065: import java.util.Iterator;
066: import java.util.ArrayList;
067:
068: import jj2000.j2k.quantization.dequantizer.*;
069: import jj2000.j2k.wavelet.synthesis.*;
070: import jj2000.j2k.image.invcomptransf.*;
071: import jj2000.j2k.fileformat.reader.*;
072: import jj2000.j2k.codestream.reader.*;
073: import jj2000.j2k.entropy.decoder.*;
074: import jj2000.j2k.codestream.*;
075: import jj2000.j2k.decoder.*;
076: import jj2000.j2k.image.*;
077: import jj2000.j2k.util.*;
078: import jj2000.j2k.roi.*;
079: import jj2000.j2k.io.*;
080: import jj2000.j2k.*;
081:
082: /** This class is the Java Image IO plugin reader for JPEG 2000 JP2 image file
083: * format. It has the capability to load the compressed bilevel images,
084: * color-indexed byte images, or multi-band images in byte/ushort/short/int
085: * data type. It may subsample the image, select bands, clip the image,
086: * and shift the decoded image origin if the proper decoding parameter
087: * are set in the provided <code>J2KImageReadParam</code>.
088: */
089: public class J2KImageReader extends ImageReader implements MsgLogger {
090: /** The input stream where reads from */
091: private ImageInputStream iis = null;
092:
093: /** Stream position when setInput() was called. */
094: private long streamPosition0;
095:
096: /** Indicates whether the header is read. */
097: private boolean gotHeader = false;
098:
099: /** The image width. */
100: private int width;
101:
102: /** The image height. */
103: private int height;
104:
105: /** Image metadata, valid for the imageMetadataIndex only. */
106: private J2KMetadata imageMetadata = null;
107:
108: /** The image index for the cached metadata. */
109: private int imageMetadataIndex = -1;
110:
111: /** The J2K HeaderDecoder defined in jj2000 packages. Used to extract image
112: * header information.
113: */
114: private HeaderDecoder hd;
115:
116: /** The J2KReadState for this reading session based on the current input
117: * and J2KImageReadParam.
118: */
119: private J2KReadState readState = null;
120:
121: /**
122: * Whether to log JJ2000 messages.
123: */
124: private boolean logJJ2000Msg = false;
125:
126: /** Wrapper for the protected method <code>computeRegions</code>. So it
127: * can be access from the classes which are not in <code>ImageReader</code>
128: * hierarchy.
129: */
130: public static void computeRegionsWrapper(ImageReadParam param,
131: boolean allowZeroDestOffset, int srcWidth, int srcHeight,
132: BufferedImage image, Rectangle srcRegion,
133: Rectangle destRegion) {
134: if (srcRegion == null) {
135: throw new IllegalArgumentException(I18N
136: .getString("J2KImageReader0"));
137: }
138: if (destRegion == null) {
139: throw new IllegalArgumentException(I18N
140: .getString("J2KImageReader1"));
141: }
142:
143: // Clip that to the param region, if there is one
144: int periodX = 1;
145: int periodY = 1;
146: int gridX = 0;
147: int gridY = 0;
148: if (param != null) {
149: Rectangle paramSrcRegion = param.getSourceRegion();
150: if (paramSrcRegion != null) {
151: srcRegion.setBounds(srcRegion
152: .intersection(paramSrcRegion));
153: }
154: periodX = param.getSourceXSubsampling();
155: periodY = param.getSourceYSubsampling();
156: gridX = param.getSubsamplingXOffset();
157: gridY = param.getSubsamplingYOffset();
158: srcRegion.translate(gridX, gridY);
159: srcRegion.width -= gridX;
160: srcRegion.height -= gridY;
161: if (allowZeroDestOffset) {
162: destRegion.setLocation(param.getDestinationOffset());
163: } else {
164: Point destOffset = param.getDestinationOffset();
165: if (destOffset.x != 0 || destOffset.y != 0) {
166: destRegion
167: .setLocation(param.getDestinationOffset());
168: }
169: }
170: }
171:
172: // Now clip any negative destination offsets, i.e. clip
173: // to the top and left of the destination image
174: if (destRegion.x < 0) {
175: int delta = -destRegion.x * periodX;
176: srcRegion.x += delta;
177: srcRegion.width -= delta;
178: destRegion.x = 0;
179: }
180: if (destRegion.y < 0) {
181: int delta = -destRegion.y * periodY;
182: srcRegion.y += delta;
183: srcRegion.height -= delta;
184: destRegion.y = 0;
185: }
186:
187: // Now clip the destination Region to the subsampled width and height
188: int subsampledWidth = (srcRegion.width + periodX - 1) / periodX;
189: int subsampledHeight = (srcRegion.height + periodY - 1)
190: / periodY;
191: destRegion.width = subsampledWidth;
192: destRegion.height = subsampledHeight;
193:
194: // Now clip that to right and bottom of the destination image,
195: // if there is one, taking subsampling into account
196: if (image != null) {
197: Rectangle destImageRect = new Rectangle(0, 0, image
198: .getWidth(), image.getHeight());
199: destRegion
200: .setBounds(destRegion.intersection(destImageRect));
201: if (destRegion.isEmpty()) {
202: throw new IllegalArgumentException(I18N
203: .getString("J2KImageReader2"));
204: }
205:
206: int deltaX = destRegion.x + subsampledWidth
207: - image.getWidth();
208: if (deltaX > 0) {
209: srcRegion.width -= deltaX * periodX;
210: }
211: int deltaY = destRegion.y + subsampledHeight
212: - image.getHeight();
213: if (deltaY > 0) {
214: srcRegion.height -= deltaY * periodY;
215: }
216: }
217: if (srcRegion.isEmpty() || destRegion.isEmpty()) {
218: throw new IllegalArgumentException(I18N
219: .getString("J2KImageReader3"));
220: }
221: }
222:
223: /** Wrapper for the protected method <code>checkReadParamBandSettings</code>.
224: * So it can be access from the classes which are not in
225: * <code>ImageReader</code> hierarchy.
226: */
227: public static void checkReadParamBandSettingsWrapper(
228: ImageReadParam param, int numSrcBands, int numDstBands) {
229: checkReadParamBandSettings(param, numSrcBands, numDstBands);
230: }
231:
232: /**
233: * Convert a rectangle provided in the coordinate system of the JPEG2000
234: * reference grid to coordinates at a lower resolution level where zero
235: * denotes the lowest resolution level.
236: *
237: * @param r A rectangle in references grid coordinates.
238: * @param maxLevel The highest resolution level in the image.
239: * @param level The resolution level of the returned rectangle.
240: * @param subX The horizontal subsampling step size.
241: * @param subY The vertical subsampling step size.
242: * @return The parameter rectangle converted to a lower resolution level.
243: * @throws IllegalArgumentException if <core>r</code> is <code>null</code>,
244: * <code>maxLevel</code> or <code>level</code> is negative, or
245: * <code>level</code> is greater than <code>maxLevel</code>.
246: */
247: static Rectangle getReducedRect(Rectangle r, int maxLevel,
248: int level, int subX, int subY) {
249: if (r == null) {
250: throw new IllegalArgumentException("r == null!");
251: } else if (maxLevel < 0 || level < 0) {
252: throw new IllegalArgumentException(
253: "maxLevel < 0 || level < 0!");
254: } else if (level > maxLevel) {
255: throw new IllegalArgumentException("level > maxLevel");
256: }
257:
258: // At the highest level; return the parameter.
259: if (level == maxLevel && subX == 1 && subY == 1) {
260: return r;
261: }
262:
263: // Resolution divisor is step*2^(maxLevel - level).
264: int divisor = 1 << (maxLevel - level);
265: int divX = divisor * subX;
266: int divY = divisor * subY;
267:
268: // Convert upper left and lower right corners.
269: int x1 = (r.x + divX - 1) / divX;
270: int x2 = (r.x + r.width + divX - 1) / divX;
271: int y1 = (r.y + divY - 1) / divY;
272: int y2 = (r.y + r.height + divY - 1) / divY;
273:
274: // Create lower resolution rectangle and return.
275: return new Rectangle(x1, y1, x2 - x1, y2 - y1);
276: }
277:
278: /** Wrapper for the protected method <code>processImageUpdate</code>
279: * So it can be access from the classes which are not in
280: * <code>ImageReader</code> hierarchy.
281: */
282: public void processImageUpdateWrapper(BufferedImage theImage,
283: int minX, int minY, int width, int height, int periodX,
284: int periodY, int[] bands) {
285: processImageUpdate(theImage, minX, minY, width, height,
286: periodX, periodY, bands);
287: }
288:
289: /** Wrapper for the protected method <code>processImageProgress</code>
290: * So it can be access from the classes which are not in
291: * <code>ImageReader</code> hierarchy.
292: */
293: public void processImageProgressWrapper(float percentageDone) {
294: processImageProgress(percentageDone);
295: }
296:
297: /** Constructs <code>J2KImageReader</code> from the provided
298: * <code>ImageReaderSpi</code>.
299: */
300: public J2KImageReader(ImageReaderSpi originator) {
301: super (originator);
302:
303: this .logJJ2000Msg = Boolean
304: .getBoolean("jj2000.j2k.decoder.log");
305:
306: FacilityManager.registerMsgLogger(null, this );
307: }
308:
309: /** Overrides the method defined in the superclass. */
310: public void setInput(Object input, boolean seekForwardOnly,
311: boolean ignoreMetadata) {
312: super .setInput(input, seekForwardOnly, ignoreMetadata);
313: this .ignoreMetadata = ignoreMetadata;
314: iis = (ImageInputStream) input; // Always works
315: imageMetadata = null;
316: try {
317: this .streamPosition0 = iis.getStreamPosition();
318: } catch (IOException e) {
319: // XXX ignore
320: }
321: }
322:
323: /** Overrides the method defined in the superclass. */
324: public int getNumImages(boolean allowSearch) throws IOException {
325: return 1;
326: }
327:
328: public int getWidth(int imageIndex) throws IOException {
329: checkIndex(imageIndex);
330: readHeader();
331: return width;
332: }
333:
334: public int getHeight(int imageIndex) throws IOException {
335: checkIndex(imageIndex);
336: readHeader();
337: return height;
338: }
339:
340: public int getTileGridXOffset(int imageIndex) throws IOException {
341: checkIndex(imageIndex);
342: readHeader();
343: return hd.getTilingOrigin(null).x;
344: }
345:
346: public int getTileGridYOffset(int imageIndex) throws IOException {
347: checkIndex(imageIndex);
348: readHeader();
349: return hd.getTilingOrigin(null).y;
350: }
351:
352: public int getTileWidth(int imageIndex) throws IOException {
353: checkIndex(imageIndex);
354: readHeader();
355: return hd.getNomTileWidth();
356: }
357:
358: public int getTileHeight(int imageIndex) throws IOException {
359: checkIndex(imageIndex);
360: readHeader();
361: return hd.getNomTileHeight();
362: }
363:
364: private void checkIndex(int imageIndex) {
365: if (imageIndex != 0) {
366: throw new IndexOutOfBoundsException(I18N
367: .getString("J2KImageReader4"));
368: }
369: }
370:
371: public void readHeader() {
372: if (gotHeader)
373: return;
374:
375: if (readState == null) {
376: try {
377: iis.seek(streamPosition0);
378: } catch (IOException e) {
379: // XXX ignore
380: }
381:
382: readState = new J2KReadState(iis,
383: new J2KImageReadParamJava(getDefaultReadParam()),
384: this );
385: }
386:
387: hd = readState.getHeader();
388: gotHeader = true;
389:
390: this .width = hd.getImgWidth();
391: this .height = hd.getImgHeight();
392: }
393:
394: public Iterator getImageTypes(int imageIndex) throws IOException {
395: checkIndex(imageIndex);
396: readHeader();
397: if (readState != null) {
398: ArrayList list = new ArrayList();
399: list.add(new ImageTypeSpecifier(readState.getColorModel(),
400: readState.getSampleModel()));
401: return list.iterator();
402: }
403: return null;
404: }
405:
406: public ImageReadParam getDefaultReadParam() {
407: return new J2KImageReadParam();
408: }
409:
410: public IIOMetadata getImageMetadata(int imageIndex)
411: throws IOException {
412: checkIndex(imageIndex);
413: if (ignoreMetadata)
414: return null;
415:
416: if (imageMetadata == null) {
417: iis.mark();
418: imageMetadata = new J2KMetadata(iis, this );
419: iis.reset();
420: }
421: return imageMetadata;
422: }
423:
424: public IIOMetadata getStreamMetadata() throws IOException {
425: return null;
426: }
427:
428: public BufferedImage read(int imageIndex, ImageReadParam param)
429: throws IOException {
430: checkIndex(imageIndex);
431: clearAbortRequest();
432: processImageStarted(imageIndex);
433:
434: if (param == null)
435: param = getDefaultReadParam();
436:
437: param = new J2KImageReadParamJava(param);
438:
439: if (!ignoreMetadata) {
440: imageMetadata = new J2KMetadata();
441: iis.seek(streamPosition0);
442: readState = new J2KReadState(iis,
443: (J2KImageReadParamJava) param, imageMetadata, this );
444: } else {
445: iis.seek(streamPosition0);
446: readState = new J2KReadState(iis,
447: (J2KImageReadParamJava) param, this );
448: }
449:
450: BufferedImage bi = readState.readBufferedImage();
451: if (abortRequested())
452: processReadAborted();
453: else
454: processImageComplete();
455: return bi;
456: }
457:
458: public RenderedImage readAsRenderedImage(int imageIndex,
459: ImageReadParam param) throws IOException {
460: checkIndex(imageIndex);
461: RenderedImage ri = null;
462: clearAbortRequest();
463: processImageStarted(imageIndex);
464:
465: if (param == null)
466: param = getDefaultReadParam();
467:
468: param = new J2KImageReadParamJava(param);
469: if (!ignoreMetadata) {
470: if (imageMetadata == null)
471: imageMetadata = new J2KMetadata();
472: ri = new J2KRenderedImage(iis,
473: (J2KImageReadParamJava) param, imageMetadata, this );
474: } else
475: ri = new J2KRenderedImage(iis,
476: (J2KImageReadParamJava) param, this );
477: if (abortRequested())
478: processReadAborted();
479: else
480: processImageComplete();
481: return ri;
482: }
483:
484: public boolean canReadRaster() {
485: return true;
486: }
487:
488: public boolean isRandomAccessEasy(int imageIndex)
489: throws IOException {
490: checkIndex(imageIndex);
491: return false;
492: }
493:
494: public Raster readRaster(int imageIndex, ImageReadParam param)
495: throws IOException {
496: checkIndex(imageIndex);
497: processImageStarted(imageIndex);
498: param = new J2KImageReadParamJava(param);
499:
500: if (!ignoreMetadata) {
501: imageMetadata = new J2KMetadata();
502: iis.seek(streamPosition0);
503: readState = new J2KReadState(iis,
504: (J2KImageReadParamJava) param, imageMetadata, this );
505: } else {
506: iis.seek(streamPosition0);
507: readState = new J2KReadState(iis,
508: (J2KImageReadParamJava) param, this );
509: }
510:
511: Raster ras = readState.readAsRaster();
512: if (abortRequested())
513: processReadAborted();
514: else
515: processImageComplete();
516: return ras;
517: }
518:
519: public boolean isImageTiled(int imageIndex) {
520: checkIndex(imageIndex);
521: readHeader();
522: if (readState != null) {
523: RenderedImage image = new J2KRenderedImage(readState);
524: if (image.getNumXTiles() * image.getNumYTiles() > 0)
525: return true;
526: return false;
527: }
528: return false;
529: }
530:
531: public void reset() {
532: // reset local Java structures
533: super .reset();
534:
535: iis = null;
536: gotHeader = false;
537: imageMetadata = null;
538: readState = null;
539: System.gc();
540: }
541:
542: /** This method wraps the protected method <code>abortRequested</code>
543: * to allow the abortions be monitored by <code>J2KReadState</code>.
544: */
545: public boolean getAbortRequest() {
546: return abortRequested();
547: }
548:
549: private ImageTypeSpecifier getImageType(int imageIndex)
550: throws IOException {
551: checkIndex(imageIndex);
552: readHeader();
553: if (readState != null) {
554: return new ImageTypeSpecifier(readState.getColorModel(),
555: readState.getSampleModel());
556: }
557: return null;
558: }
559:
560: // --- Begin jj2000.j2k.util.MsgLogger implementation ---
561: public void flush() {
562: // Do nothing.
563: }
564:
565: public void println(String str, int flind, int ind) {
566: printmsg(INFO, str);
567: }
568:
569: public void printmsg(int sev, String msg) {
570: if (logJJ2000Msg) {
571: String msgSev;
572: switch (sev) {
573: case ERROR:
574: msgSev = "ERROR";
575: break;
576: case INFO:
577: msgSev = "INFO";
578: break;
579: case LOG:
580: msgSev = "LOG";
581: break;
582: case WARNING:
583: default:
584: msgSev = "WARNING";
585: break;
586: }
587:
588: processWarningOccurred("[JJ2000 " + msgSev + "] " + msg);
589: }
590: }
591: // --- End jj2000.j2k.util.MsgLogger implementation ---
592: }
|