001: /*
002: * $RCSfile: JPEGImageDecoder.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.3 $
009: * $Date: 2006/08/22 00:12:04 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.codecimpl;
013:
014: import java.awt.Graphics2D;
015: import java.awt.Point;
016: import java.awt.RenderingHints;
017: import java.awt.geom.AffineTransform;
018: import java.awt.image.BufferedImage;
019: import java.awt.image.ComponentSampleModel;
020: import java.awt.image.Raster;
021: import java.awt.image.RenderedImage;
022: import java.io.FilterInputStream;
023: import java.io.InputStream;
024: import java.io.IOException;
025: import com.sun.media.jai.codec.ImageDecoderImpl;
026: import com.sun.media.jai.codec.ImageDecodeParam;
027: import com.sun.media.jai.codec.JPEGDecodeParam;
028: import com.sun.media.jai.codecimpl.ImagingListenerProxy;
029: import com.sun.media.jai.codecimpl.util.ImagingException;
030: import com.sun.image.codec.jpeg.ImageFormatException;
031:
032: /**
033: * @since EA2
034: */
035: public class JPEGImageDecoder extends ImageDecoderImpl {
036:
037: public JPEGImageDecoder(InputStream input, ImageDecodeParam param) {
038: super (input, param);
039: }
040:
041: public RenderedImage decodeAsRenderedImage(int page)
042: throws IOException {
043: if (page != 0) {
044: throw new IOException(JaiI18N
045: .getString("JPEGImageDecoder0"));
046: }
047: try {
048: return new JPEGImage(input, param);
049: } catch (Exception e) {
050: throw CodecUtils.toIOException(e);
051: }
052: }
053: }
054:
055: /**
056: * FilterInputStream subclass which does not support mark/reset.
057: * Used to work around a failure of com.sun.image.codec.jpeg.JPEGImageDecoder
058: * in which decodeAsBufferedImage() blocks in reset() if a corrupted JPEG
059: * image is encountered.
060: */
061: class NoMarkStream extends FilterInputStream {
062: NoMarkStream(InputStream in) {
063: super (in);
064: }
065:
066: /**
067: * Disallow mark/reset.
068: */
069: public boolean markSupported() {
070: return false;
071: }
072:
073: /**
074: * Disallow close() from closing the stream passed in.
075: */
076: public final void close() throws IOException {
077: // Deliberately do nothing.
078: }
079: }
080:
081: class JPEGImage extends SimpleRenderedImage {
082:
083: /**
084: * Mutex for the entire class to circumvent thread unsafety of
085: * com.sun.image.codec.jpeg.JPEGImageDecoder implementation.
086: */
087: private static final Object LOCK = new Object();
088:
089: private Raster theTile = null;
090:
091: /**
092: * Construct a JPEGmage.
093: *
094: * @param stream The JPEG InputStream.
095: * @param param The decoding parameters.
096: */
097: public JPEGImage(InputStream stream, ImageDecodeParam param) {
098: // If the supplied InputStream supports mark/reset wrap it so
099: // it does not.
100: if (stream.markSupported()) {
101: stream = new NoMarkStream(stream);
102: }
103:
104: // Lock the entire class to work around lack of thread safety
105: // in com.sun.image.codec.jpeg.JPEGImageDecoder implementation.
106: BufferedImage image = null;
107: synchronized (LOCK) {
108: com.sun.image.codec.jpeg.JPEGImageDecoder decoder = com.sun.image.codec.jpeg.JPEGCodec
109: .createJPEGDecoder(stream);
110: try {
111: // decodeAsBufferedImage performs default color conversions
112: image = decoder.decodeAsBufferedImage();
113: } catch (ImageFormatException e) {
114: String message = JaiI18N.getString("JPEGImageDecoder1");
115: sendExceptionToListener(message, (Exception) e);
116: // throw new RuntimeException(JaiI18N.getString("JPEGImageDecoder1"));
117: } catch (IOException e) {
118: String message = JaiI18N.getString("JPEGImageDecoder1");
119: sendExceptionToListener(message, (Exception) e);
120: // throw new RuntimeException(JaiI18N.getString("JPEGImageDecoder2"));
121: }
122: }
123:
124: minX = 0;
125: minY = 0;
126: tileWidth = width = image.getWidth();
127: tileHeight = height = image.getHeight();
128:
129: // Force image to have a ComponentSampleModel if it does not have one
130: // and the ImageDecodeParam is either null or is a JPEGDecodeParam
131: // with 'decodeToCSM' set to 'true'.
132: if ((param == null || (param instanceof JPEGDecodeParam && ((JPEGDecodeParam) param)
133: .getDecodeToCSM()))
134: && !(image.getSampleModel() instanceof ComponentSampleModel)) {
135:
136: int type = -1;
137: int numBands = image.getSampleModel().getNumBands();
138: if (numBands == 1) {
139: type = BufferedImage.TYPE_BYTE_GRAY;
140: } else if (numBands == 3) {
141: type = BufferedImage.TYPE_3BYTE_BGR;
142: } else if (numBands == 4) {
143: type = BufferedImage.TYPE_4BYTE_ABGR;
144: } else {
145: throw new RuntimeException(JaiI18N
146: .getString("JPEGImageDecoder3"));
147: }
148:
149: BufferedImage bi = new BufferedImage(width, height, type);
150: bi.getWritableTile(0, 0).setRect(
151: image.getWritableTile(0, 0));
152: bi.releaseWritableTile(0, 0);
153: image = bi;
154: }
155:
156: sampleModel = image.getSampleModel();
157: colorModel = image.getColorModel();
158:
159: theTile = image.getWritableTile(0, 0);
160: }
161:
162: public synchronized Raster getTile(int tileX, int tileY) {
163: if (tileX != 0 || tileY != 0) {
164: throw new IllegalArgumentException(JaiI18N
165: .getString("JPEGImageDecoder4"));
166: }
167:
168: return theTile;
169: }
170:
171: public void dispose() {
172: theTile = null;
173: }
174:
175: private void sendExceptionToListener(String message, Exception e) {
176: ImagingListenerProxy.errorOccurred(message,
177: new ImagingException(message, e), this , false);
178: }
179: }
|