001: /*
002: * $RCSfile: WBMPImageReader.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.2 $
042: * $Date: 2006/04/21 00:02:26 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.wbmp;
046:
047: import java.awt.Rectangle;
048: import java.awt.image.BufferedImage;
049: import java.awt.image.DataBufferByte;
050: import java.awt.image.MultiPixelPackedSampleModel;
051: import java.awt.image.Raster;
052: import java.awt.image.WritableRaster;
053:
054: import javax.imageio.IIOException;
055: import javax.imageio.ImageReader;
056: import javax.imageio.ImageReadParam;
057: import javax.imageio.ImageTypeSpecifier;
058: import javax.imageio.metadata.IIOMetadata;
059: import javax.imageio.spi.ImageReaderSpi;
060: import javax.imageio.stream.ImageInputStream;
061:
062: import java.io.*;
063: import java.util.ArrayList;
064: import java.util.Iterator;
065:
066: import com.sun.media.imageioimpl.common.ImageUtil;
067:
068: /** This class is the Java Image IO plugin reader for WBMP images.
069: * It may subsample the image, clip the image,
070: * and shift the decoded image origin if the proper decoding parameter
071: * are set in the provided <code>WBMPImageReadParam</code>.
072: */
073: public class WBMPImageReader extends ImageReader {
074: /** The input stream where reads from */
075: private ImageInputStream iis = null;
076:
077: /** Indicates whether the header is read. */
078: private boolean gotHeader = false;
079:
080: /** The stream position where the image data starts. */
081: private long imageDataOffset;
082:
083: /** The original image width. */
084: private int width;
085:
086: /** The original image height. */
087: private int height;
088:
089: private int wbmpType;
090:
091: private WBMPMetadata metadata;
092:
093: /** Constructs <code>WBMPImageReader</code> from the provided
094: * <code>ImageReaderSpi</code>.
095: */
096: public WBMPImageReader(ImageReaderSpi originator) {
097: super (originator);
098: }
099:
100: /** Overrides the method defined in the superclass. */
101: public void setInput(Object input, boolean seekForwardOnly,
102: boolean ignoreMetadata) {
103: super .setInput(input, seekForwardOnly, ignoreMetadata);
104: iis = (ImageInputStream) input; // Always works
105: gotHeader = false;
106: }
107:
108: /** Overrides the method defined in the superclass. */
109: public int getNumImages(boolean allowSearch) throws IOException {
110: if (iis == null) {
111: throw new IllegalStateException(I18N
112: .getString("GetNumImages0"));
113: }
114: if (seekForwardOnly && allowSearch) {
115: throw new IllegalStateException(I18N
116: .getString("GetNumImages1"));
117: }
118: return 1;
119: }
120:
121: public int getWidth(int imageIndex) throws IOException {
122: checkIndex(imageIndex);
123: readHeader();
124: return width;
125: }
126:
127: public int getHeight(int imageIndex) throws IOException {
128: checkIndex(imageIndex);
129: readHeader();
130: return height;
131: }
132:
133: public boolean isRandomAccessEasy(int imageIndex)
134: throws IOException {
135: checkIndex(imageIndex);
136: return true;
137: }
138:
139: private void checkIndex(int imageIndex) {
140: if (imageIndex != 0) {
141: throw new IndexOutOfBoundsException(I18N
142: .getString("WBMPImageReader0"));
143: }
144: }
145:
146: public void readHeader() throws IOException {
147: if (gotHeader) {
148: // Seek to where the image data starts, since that is where
149: // the stream pointer should be after header is read
150: iis.seek(imageDataOffset);
151: return;
152: }
153:
154: if (iis == null) {
155: throw new IllegalStateException(I18N
156: .getString("WBMPImageReader1"));
157: }
158:
159: metadata = new WBMPMetadata();
160: wbmpType = iis.readByte(); // TypeField
161: byte fixHeaderField = iis.readByte();
162:
163: // check for valid wbmp image
164: if (fixHeaderField != 0 || !isValidWbmpType(wbmpType)) {
165: throw new IIOException(I18N.getString("WBMPImageReader2"));
166: }
167:
168: metadata.wbmpType = wbmpType;
169:
170: // Read image width
171: width = ImageUtil.readMultiByteInteger(iis);
172: metadata.width = width;
173:
174: // Read image height
175: height = ImageUtil.readMultiByteInteger(iis);
176: metadata.height = height;
177:
178: gotHeader = true;
179: // Store the stream position where the image data starts
180: imageDataOffset = iis.getStreamPosition();
181: }
182:
183: public Iterator getImageTypes(int imageIndex) throws IOException {
184: checkIndex(imageIndex);
185: readHeader();
186:
187: BufferedImage bi = new BufferedImage(1, 1,
188: BufferedImage.TYPE_BYTE_BINARY);
189: ArrayList list = new ArrayList(1);
190: list.add(new ImageTypeSpecifier(bi));
191: return list.iterator();
192: }
193:
194: public ImageReadParam getDefaultReadParam() {
195: return new ImageReadParam();
196: }
197:
198: public IIOMetadata getImageMetadata(int imageIndex)
199: throws IOException {
200: checkIndex(imageIndex);
201: if (metadata == null) {
202: readHeader();
203: }
204: return metadata;
205: }
206:
207: public IIOMetadata getStreamMetadata() throws IOException {
208: return null;
209: }
210:
211: public BufferedImage read(int imageIndex, ImageReadParam param)
212: throws IOException {
213:
214: if (iis == null) {
215: throw new IllegalStateException(I18N
216: .getString("WBMPImageReader1"));
217: }
218:
219: checkIndex(imageIndex);
220: clearAbortRequest();
221: processImageStarted(imageIndex);
222: if (param == null)
223: param = getDefaultReadParam();
224:
225: //read header
226: readHeader();
227:
228: Rectangle sourceRegion = new Rectangle(0, 0, 0, 0);
229: Rectangle destinationRegion = new Rectangle(0, 0, 0, 0);
230:
231: computeRegions(param, this .width, this .height, param
232: .getDestination(), sourceRegion, destinationRegion);
233:
234: int scaleX = param.getSourceXSubsampling();
235: int scaleY = param.getSourceYSubsampling();
236: int xOffset = param.getSubsamplingXOffset();
237: int yOffset = param.getSubsamplingYOffset();
238:
239: // If the destination is provided, then use it. Otherwise, create new one
240: BufferedImage bi = param.getDestination();
241:
242: if (bi == null)
243: bi = new BufferedImage(destinationRegion.x
244: + destinationRegion.width, destinationRegion.y
245: + destinationRegion.height,
246: BufferedImage.TYPE_BYTE_BINARY);
247:
248: boolean noTransform = destinationRegion.equals(new Rectangle(0,
249: 0, width, height))
250: && destinationRegion.equals(new Rectangle(0, 0, bi
251: .getWidth(), bi.getHeight()));
252:
253: // Get the image data.
254: WritableRaster tile = bi.getWritableTile(0, 0);
255:
256: // Get the SampleModel.
257: MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel) bi
258: .getSampleModel();
259:
260: if (noTransform) {
261: if (abortRequested()) {
262: processReadAborted();
263: return bi;
264: }
265:
266: // If noTransform is necessary, read the data.
267: iis.read(((DataBufferByte) tile.getDataBuffer()).getData(),
268: 0, height * sm.getScanlineStride());
269: processImageUpdate(bi, 0, 0, width, height, 1, 1,
270: new int[] { 0 });
271: processImageProgress(100.0F);
272: } else {
273: int len = (this .width + 7) / 8;
274: byte[] buf = new byte[len];
275: byte[] data = ((DataBufferByte) tile.getDataBuffer())
276: .getData();
277: int lineStride = sm.getScanlineStride();
278: iis.skipBytes(len * sourceRegion.y);
279: int skipLength = len * (scaleY - 1);
280:
281: // cache the values to avoid duplicated computation
282: int[] srcOff = new int[destinationRegion.width];
283: int[] destOff = new int[destinationRegion.width];
284: int[] srcPos = new int[destinationRegion.width];
285: int[] destPos = new int[destinationRegion.width];
286:
287: for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; i < destinationRegion.x
288: + destinationRegion.width; i++, j++, x += scaleX) {
289: srcPos[j] = x >> 3;
290: srcOff[j] = 7 - (x & 7);
291: destPos[j] = i >> 3;
292: destOff[j] = 7 - (i & 7);
293: }
294:
295: for (int j = 0, y = sourceRegion.y, k = destinationRegion.y
296: * lineStride; j < destinationRegion.height; j++, y += scaleY) {
297:
298: if (abortRequested())
299: break;
300: iis.read(buf, 0, len);
301: for (int i = 0; i < destinationRegion.width; i++) {
302: //get the bit and assign to the data buffer of the raster
303: int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
304: data[k + destPos[i]] |= v << destOff[i];
305: }
306:
307: k += lineStride;
308: iis.skipBytes(skipLength);
309: processImageUpdate(bi, 0, j, destinationRegion.width,
310: 1, 1, 1, new int[] { 0 });
311: processImageProgress(100.0F * j
312: / destinationRegion.height);
313: }
314: }
315:
316: if (abortRequested())
317: processReadAborted();
318: else
319: processImageComplete();
320: return bi;
321: }
322:
323: public boolean canReadRaster() {
324: return true;
325: }
326:
327: public Raster readRaster(int imageIndex, ImageReadParam param)
328: throws IOException {
329: BufferedImage bi = read(imageIndex, param);
330: return bi.getData();
331: }
332:
333: public void reset() {
334: super .reset();
335: iis = null;
336: gotHeader = false;
337: }
338:
339: /*
340: * This method verifies that given byte is valid wbmp type marker.
341: * At the moment only 0x0 marker is described by wbmp spec.
342: */
343: boolean isValidWbmpType(int type) {
344: return type == 0;
345: }
346: }
|