001: /*
002: * $RCSfile: J2KImageReaderCodecLib.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.4 $
042: * $Date: 2006/09/29 20:19:55 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.jpeg2000;
046:
047: import com.sun.media.imageio.plugins.jpeg2000.J2KImageReadParam;
048:
049: import java.awt.Point;
050: import java.awt.image.ColorModel;
051: import java.awt.image.Raster;
052: import java.awt.image.RenderedImage;
053: import java.awt.image.SampleModel;
054: import java.awt.image.WritableRaster;
055: import java.io.IOException;
056: import java.util.Hashtable;
057:
058: import javax.imageio.IIOException;
059: import javax.imageio.ImageReader;
060: import javax.imageio.ImageReadParam;
061: import javax.imageio.ImageTypeSpecifier;
062: import javax.imageio.metadata.IIOMetadata;
063: import javax.imageio.spi.ImageReaderSpi;
064: import javax.imageio.stream.ImageInputStream;
065:
066: import java.util.ArrayList;
067: import java.util.Iterator;
068: import javax.imageio.metadata.IIOMetadata;
069: import java.awt.image.BufferedImage;
070:
071: import com.sun.media.imageioimpl.common.SimpleRenderedImage;
072:
073: import com.sun.medialib.codec.jp2k.Decoder;
074: import com.sun.medialib.codec.jp2k.Size;
075: import com.sun.medialib.codec.jiio.*;
076:
077: public class J2KImageReaderCodecLib extends ImageReader {
078: /** Wrapper for the protected method <code>processImageUpdate</code>
079: * So it can be access from the classes which are not in
080: * <code>ImageReader</code> hierachy.
081: */
082: public void processImageUpdateWrapper(BufferedImage theImage,
083: int minX, int minY, int width, int height, int periodX,
084: int periodY, int[] bands) {
085: processImageUpdate(theImage, minX, minY, width, height,
086: periodX, periodY, bands);
087: }
088:
089: /** Wrapper for the protected method <code>processImageProgress</code>
090: * So it can be access from the classes which are not in
091: * <code>ImageReader</code> hierachy.
092: */
093: public void processImageProgressWrapper(float percentageDone) {
094: processImageProgress(percentageDone);
095: }
096:
097: /** The input stream where reads from */
098: private ImageInputStream iis = null;
099:
100: /** Stream position when setInput() was called. */
101: private long streamPosition0;
102:
103: /** Indicates whether the header is read. */
104: private boolean gotHeader = false;
105:
106: /** The image width. */
107: private int width = -1;
108:
109: /** The image height. */
110: private int height = -1;
111:
112: /** The image tile width. */
113: private int tileWidth = -1;
114:
115: /** The image tile height. */
116: private int tileHeight = -1;
117:
118: /** The image tile grid X offset. */
119: private int tileGridXOffset = 0;
120:
121: /** The image tile grid Y offset. */
122: private int tileGridYOffset = 0;
123:
124: /** Image metadata, valid for the imageMetadataIndex only. */
125: private J2KMetadata imageMetadata = null;
126:
127: /** The RenderedImage decoded from the stream. */
128: SimpleRenderedImage image = null;
129:
130: public J2KImageReaderCodecLib(ImageReaderSpi originator) {
131: super (originator);
132: }
133:
134: /** Overrides the method defined in the superclass. */
135: public void setInput(Object input, boolean seekForwardOnly,
136: boolean ignoreMetadata) {
137: super .setInput(input, seekForwardOnly, ignoreMetadata);
138: this .ignoreMetadata = ignoreMetadata;
139: iis = (ImageInputStream) input; // Always works
140: iis.mark(); // Mark the initial position.
141: imageMetadata = null;
142: try {
143: this .streamPosition0 = iis.getStreamPosition();
144: } catch (IOException e) {
145: // XXX ignore
146: }
147: }
148:
149: public ImageReadParam getDefaultReadParam() {
150: return new J2KImageReadParam();
151: }
152:
153: public int getNumImages(boolean allowSearch)
154: throws java.io.IOException {
155: if (input == null) {
156: throw new IllegalStateException(I18N
157: .getString("J2KImageReader6"));
158: } else if (seekForwardOnly) {
159: throw new IllegalStateException(I18N
160: .getString("J2KImageReader7"));
161: }
162:
163: return 1;
164: }
165:
166: public Iterator getImageTypes(int imageIndex)
167: throws java.io.IOException {
168: checkIndex(imageIndex);
169: readHeader();
170: if (image != null) {
171: ArrayList list = new ArrayList();
172: list.add(new ImageTypeSpecifier(image.getColorModel(),
173: image.getSampleModel()));
174: return list.iterator();
175: }
176: return null;
177: }
178:
179: public int getWidth(int imageIndex) throws java.io.IOException {
180: checkIndex(imageIndex);
181: readHeader();
182: return width;
183: }
184:
185: public int getHeight(int imageIndex) throws java.io.IOException {
186: checkIndex(imageIndex);
187: readHeader();
188: return height;
189: }
190:
191: public int getTileGridXOffset(int imageIndex) throws IOException {
192: checkIndex(imageIndex);
193: readHeader();
194: return tileGridXOffset;
195: }
196:
197: public int getTileGridYOffset(int imageIndex) throws IOException {
198: checkIndex(imageIndex);
199: readHeader();
200: return tileGridYOffset;
201: }
202:
203: public int getTileWidth(int imageIndex) throws IOException {
204: checkIndex(imageIndex);
205: readHeader();
206: return tileWidth;
207: }
208:
209: public int getTileHeight(int imageIndex) throws IOException {
210: checkIndex(imageIndex);
211: readHeader();
212: return tileHeight;
213: }
214:
215: /**
216: * Returns <code>true</code> if the image is organized into
217: * <i>tiles</i>, that is, equal-sized non-overlapping rectangles.
218: *
219: * <p> A reader plug-in may choose whether or not to expose tiling
220: * that is present in the image as it is stored. It may even
221: * choose to advertise tiling when none is explicitly present. In
222: * general, tiling should only be advertised if there is some
223: * advantage (in speed or space) to accessing individual tiles.
224: * Regardless of whether the reader advertises tiling, it must be
225: * capable of reading an arbitrary rectangular region specified in
226: * an <code>ImageReadParam</code>.
227: *
228: * <p> A reader for which all images are guaranteed to be tiled,
229: * or are guaranteed not to be tiled, may return <code>true</code>
230: * or <code>false</code> respectively without accessing any image
231: * data. In such cases, it is not necessary to throw an exception
232: * even if no input source has been set or the image index is out
233: * of bounds.
234: *
235: * <p> The default implementation just returns <code>false</code>.
236: *
237: * @param imageIndex the index of the image to be queried.
238: *
239: * @return <code>true</code> if the image is tiled.
240: *
241: * @exception IllegalStateException if an input source is required
242: * to determine the return value, but none has been set.
243: * @exception IndexOutOfBoundsException if an image must be
244: * accessed to determine the return value, but the supplied index
245: * is out of bounds.
246: * @exception IOException if an error occurs during reading.
247: */
248: public boolean isImageTiled(int imageIndex) throws IOException {
249: int w = getWidth(imageIndex);
250: int tw = getTileWidth(imageIndex);
251: if (tw > 0 && ((w + tw - 1) / tw) > 1) {
252: return true;
253: }
254:
255: int h = getHeight(imageIndex);
256: int th = getTileHeight(imageIndex);
257: if (th > 0 && ((h + th - 1) / th) > 1) {
258: return true;
259: }
260:
261: return false;
262: }
263:
264: public IIOMetadata getStreamMetadata() throws java.io.IOException {
265: return null;
266: }
267:
268: public IIOMetadata getImageMetadata(int imageIndex)
269: throws java.io.IOException {
270: if (ignoreMetadata)
271: return null;
272:
273: checkIndex(imageIndex);
274:
275: if (imageMetadata == null) {
276: try {
277: iis.reset(); // Reset to initial position.
278: iis.mark(); // Re-mark initial position.
279: if (image == null
280: || !(image instanceof J2KRenderedImageCodecLib))
281: image = new J2KRenderedImageCodecLib(iis, this ,
282: null);
283: imageMetadata = ((J2KRenderedImageCodecLib) image)
284: .readImageMetadata();
285: } catch (IOException ioe) {
286: throw ioe;
287: } catch (RuntimeException re) {
288: throw re;
289: } finally {
290: iis.reset(); // Reset to initial position.
291: iis.mark(); // Re-mark initial position.
292: }
293: }
294: return imageMetadata;
295: }
296:
297: public boolean isRandomAccessEasy(int imageIndex)
298: throws IOException {
299: // No check done because neither input source nor image is
300: // required to determine the return value.
301: return true;
302: }
303:
304: public RenderedImage readAsRenderedImage(int imageIndex,
305: ImageReadParam param) throws java.io.IOException {
306: checkIndex(imageIndex);
307: if (param == null)
308: param = getDefaultReadParam();
309: clearAbortRequest();
310: processImageStarted(0);
311:
312: if (param instanceof J2KImageReadParam
313: && ((J2KImageReadParam) param).getResolution() >= 0) {
314: // XXX Workaround for java.sun.com change request 5089981:
315: // fall back to Java reader for lower resolution levels.
316: // This code should be removed when this problem is fixed
317: // in the codecLib JPEG2000 decoder.
318: ImageReader jreader = new J2KImageReader(null);
319: iis.seek(streamPosition0);
320: jreader.setInput(iis);
321: image = (SimpleRenderedImage) jreader.readAsRenderedImage(
322: imageIndex, param);
323: } else {
324: image = new J2KRenderedImageCodecLib(iis, this , param);
325: }
326: if (abortRequested())
327: processReadAborted();
328: else
329: processImageComplete();
330: return image;
331: }
332:
333: public BufferedImage read(int imageIndex, ImageReadParam param)
334: throws java.io.IOException {
335: checkIndex(imageIndex);
336: clearAbortRequest();
337: if (param == null)
338: param = getDefaultReadParam();
339: processImageStarted(imageIndex);
340:
341: if (param instanceof J2KImageReadParam
342: && ((J2KImageReadParam) param).getResolution() >= 0) {
343: // XXX Workaround for java.sun.com change request 5089981:
344: // fall back to Java reader for lower resolution levels.
345: // This code should be removed when this problem is fixed
346: // in the codecLib JPEG2000 decoder.
347: ImageReader jreader = new J2KImageReader(null);
348: iis.seek(streamPosition0);
349: jreader.setInput(iis);
350: if (abortRequested())
351: processReadAborted();
352: else
353: processImageComplete();
354: return jreader.read(imageIndex, param);
355: }
356:
357: BufferedImage bi = param.getDestination();
358: iis.reset(); // Reset to initial position.
359: iis.mark(); // Re-mark initial position.
360: // XXX Need to add a try-catch IOException block and reset/mark iis.
361: image = new J2KRenderedImageCodecLib(iis, this , param);
362: J2KRenderedImageCodecLib jclibImage = (J2KRenderedImageCodecLib) image;
363: Point offset = param.getDestinationOffset();
364: WritableRaster raster;
365:
366: if (bi == null) {
367: ColorModel colorModel = jclibImage.getColorModel();
368: SampleModel sampleModel = jclibImage.getSampleModel();
369:
370: // If the destination type is specified, use the color model of it.
371: ImageTypeSpecifier type = param.getDestinationType();
372: if (type != null)
373: colorModel = type.getColorModel();
374:
375: raster = Raster.createWritableRaster(sampleModel
376: .createCompatibleSampleModel(jclibImage.getMinX()
377: + jclibImage.getWidth(), jclibImage
378: .getMinY()
379: + jclibImage.getHeight()), new Point(0, 0));
380:
381: bi = new BufferedImage(colorModel, raster,
382: colorModel != null ? colorModel
383: .isAlphaPremultiplied() : false,
384: new Hashtable());
385: } else {
386: raster = bi.getWritableTile(0, 0);
387: }
388:
389: jclibImage.setDestImage(bi);
390: jclibImage.readAsRaster(raster);
391: jclibImage.clearDestImage();
392:
393: if (abortRequested())
394: processReadAborted();
395: else
396: processImageComplete();
397: return bi;
398: }
399:
400: public Raster readRaster(int imageIndex, ImageReadParam param)
401: throws IOException {
402: BufferedImage bi = read(imageIndex, param);
403: return bi.getWritableTile(0, 0);
404: }
405:
406: public void readHeader() throws java.io.IOException {
407: if (gotHeader)
408: return;
409:
410: try {
411: iis.reset(); // Reset to initial position.
412: iis.mark(); // Re-mark initial position.
413:
414: if (image == null)
415: image = new J2KRenderedImageCodecLib(iis, this , null);
416:
417: this .width = image.getWidth();
418: this .height = image.getHeight();
419: this .tileWidth = image.getTileWidth();
420: this .tileHeight = image.getTileHeight();
421: this .tileGridXOffset = image.getTileGridXOffset();
422: this .tileGridYOffset = image.getTileGridYOffset();
423: } catch (IOException ioe) {
424: throw ioe;
425: } catch (RuntimeException re) {
426: throw re;
427: } finally {
428: iis.reset(); // Reset to initial position.
429: iis.mark(); // Re-mark initial position.
430: }
431:
432: this .gotHeader = true;
433: }
434:
435: private void checkIndex(int imageIndex) {
436: if (input == null) {
437: throw new IllegalStateException(I18N
438: .getString("J2KImageReader6"));
439: }
440: if (imageIndex != 0) {
441: throw new IndexOutOfBoundsException(I18N
442: .getString("J2KImageReader4"));
443: }
444: }
445:
446: /** This method wraps the protected method <code>abortRequested</code>
447: * to allow the abortions be monitored by <code>J2KReadState</code>.
448: */
449: public boolean getAbortRequest() {
450: return abortRequested();
451: }
452:
453: public void reset() {
454: super .reset();
455: iis = null;
456: gotHeader = false;
457: width = -1;
458: height = -1;
459: tileWidth = -1;
460: tileHeight = -1;
461: tileGridXOffset = 0;
462: tileGridYOffset = 0;
463: imageMetadata = null;
464: image = null;
465: }
466: }
|