001: /*
002: * $RCSfile: WBMPImageWriter.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.1 $
042: * $Date: 2005/02/11 05:01:52 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.wbmp;
046:
047: import java.awt.Point;
048: import java.awt.Rectangle;
049: import java.awt.image.ColorModel;
050: import java.awt.image.DataBuffer;
051: import java.awt.image.DataBufferByte;
052: import java.awt.image.IndexColorModel;
053: import java.awt.image.MultiPixelPackedSampleModel;
054: import java.awt.image.Raster;
055: import java.awt.image.RenderedImage;
056: import java.awt.image.SampleModel;
057: import java.awt.image.WritableRaster;
058:
059: import java.io.IOException;
060:
061: import javax.imageio.IIOImage;
062: import javax.imageio.IIOException;
063: import javax.imageio.ImageTypeSpecifier;
064: import javax.imageio.ImageWriteParam;
065: import javax.imageio.ImageWriter;
066: import javax.imageio.metadata.IIOMetadata;
067: import javax.imageio.metadata.IIOMetadataFormatImpl;
068: import javax.imageio.metadata.IIOInvalidTreeException;
069: import javax.imageio.spi.ImageWriterSpi;
070: import javax.imageio.stream.ImageOutputStream;
071:
072: /**
073: * The Java Image IO plugin writer for encoding a binary RenderedImage into
074: * a WBMP format.
075: *
076: * The encoding process may clip, subsample using the parameters
077: * specified in the <code>ImageWriteParam</code>.
078: *
079: * @see com.sun.media.imageio.plugins.WBMPImageWriteParam
080: */
081: public class WBMPImageWriter extends ImageWriter {
082: /** The output stream to write into */
083: private ImageOutputStream stream = null;
084:
085: // Get the number of bits required to represent an int.
086: private static int getNumBits(int intValue) {
087: int numBits = 32;
088: int mask = 0x80000000;
089: while (mask != 0 && (intValue & mask) == 0) {
090: numBits--;
091: mask >>>= 1;
092: }
093: return numBits;
094: }
095:
096: // Convert an int value to WBMP multi-byte format.
097: private static byte[] intToMultiByte(int intValue) {
098: int numBitsLeft = getNumBits(intValue);
099: byte[] multiBytes = new byte[(numBitsLeft + 6) / 7];
100:
101: int maxIndex = multiBytes.length - 1;
102: for (int b = 0; b <= maxIndex; b++) {
103: multiBytes[b] = (byte) ((intValue >>> ((maxIndex - b) * 7)) & 0x7f);
104: if (b != maxIndex) {
105: multiBytes[b] |= (byte) 0x80;
106: }
107: }
108:
109: return multiBytes;
110: }
111:
112: /** Constructs <code>WBMPImageWriter</code> based on the provided
113: * <code>ImageWriterSpi</code>.
114: */
115: public WBMPImageWriter(ImageWriterSpi originator) {
116: super (originator);
117: }
118:
119: public void setOutput(Object output) {
120: super .setOutput(output); // validates output
121: if (output != null) {
122: if (!(output instanceof ImageOutputStream))
123: throw new IllegalArgumentException(I18N
124: .getString("WBMPImageWriter"));
125: this .stream = (ImageOutputStream) output;
126: } else
127: this .stream = null;
128: }
129:
130: public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
131: return null;
132: }
133:
134: public IIOMetadata getDefaultImageMetadata(
135: ImageTypeSpecifier imageType, ImageWriteParam param) {
136: WBMPMetadata meta = new WBMPMetadata();
137: meta.wbmpType = 0; // default wbmp level
138: return meta;
139: }
140:
141: public IIOMetadata convertStreamMetadata(IIOMetadata inData,
142: ImageWriteParam param) {
143: return null;
144: }
145:
146: public IIOMetadata convertImageMetadata(IIOMetadata metadata,
147: ImageTypeSpecifier type, ImageWriteParam param) {
148: return null;
149: }
150:
151: public boolean canWriteRasters() {
152: return true;
153: }
154:
155: public void write(IIOMetadata streamMetadata, IIOImage image,
156: ImageWriteParam param) throws IOException {
157: if (stream == null) {
158: throw new IllegalStateException(I18N
159: .getString("WBMPImageWriter3"));
160: }
161:
162: if (image == null) {
163: throw new IllegalArgumentException(I18N
164: .getString("WBMPImageWriter4"));
165: }
166:
167: clearAbortRequest();
168: processImageStarted(0);
169: if (param == null)
170: param = getDefaultWriteParam();
171:
172: RenderedImage input = null;
173: Raster inputRaster = null;
174: boolean writeRaster = image.hasRaster();
175: Rectangle sourceRegion = param.getSourceRegion();
176: SampleModel sampleModel = null;
177:
178: if (writeRaster) {
179: inputRaster = image.getRaster();
180: sampleModel = inputRaster.getSampleModel();
181: } else {
182: input = image.getRenderedImage();
183: sampleModel = input.getSampleModel();
184:
185: inputRaster = input.getData();
186: }
187:
188: checkSampleModel(sampleModel);
189: if (sourceRegion == null)
190: sourceRegion = inputRaster.getBounds();
191: else
192: sourceRegion = sourceRegion.intersection(inputRaster
193: .getBounds());
194:
195: if (sourceRegion.isEmpty())
196: throw new RuntimeException(I18N
197: .getString("WBMPImageWriter1"));
198:
199: int scaleX = param.getSourceXSubsampling();
200: int scaleY = param.getSourceYSubsampling();
201: int xOffset = param.getSubsamplingXOffset();
202: int yOffset = param.getSubsamplingYOffset();
203:
204: sourceRegion.translate(xOffset, yOffset);
205: sourceRegion.width -= xOffset;
206: sourceRegion.height -= yOffset;
207:
208: int minX = sourceRegion.x / scaleX;
209: int minY = sourceRegion.y / scaleY;
210: int w = (sourceRegion.width + scaleX - 1) / scaleX;
211: int h = (sourceRegion.height + scaleY - 1) / scaleY;
212:
213: Rectangle destinationRegion = new Rectangle(minX, minY, w, h);
214: sampleModel = sampleModel.createCompatibleSampleModel(w, h);
215:
216: SampleModel destSM = sampleModel;
217:
218: // If the data are not formatted nominally then reformat.
219: if (sampleModel.getDataType() != DataBuffer.TYPE_BYTE
220: || !(sampleModel instanceof MultiPixelPackedSampleModel)
221: || ((MultiPixelPackedSampleModel) sampleModel)
222: .getDataBitOffset() != 0) {
223: destSM = new MultiPixelPackedSampleModel(
224: DataBuffer.TYPE_BYTE, w, h, 1, w + 7 >> 3, 0);
225: }
226:
227: if (!destinationRegion.equals(sourceRegion)) {
228: if (scaleX == 1 && scaleY == 1)
229: inputRaster = inputRaster.createChild(inputRaster
230: .getMinX(), inputRaster.getMinY(), w, h, minX,
231: minY, null);
232: else {
233: WritableRaster ras = Raster.createWritableRaster(
234: destSM, new Point(minX, minY));
235:
236: byte[] data = ((DataBufferByte) ras.getDataBuffer())
237: .getData();
238:
239: for (int j = minY, y = sourceRegion.y, k = 0; j < minY
240: + h; j++, y += scaleY) {
241:
242: for (int i = 0, x = sourceRegion.x; i < w; i++, x += scaleX) {
243: int v = inputRaster.getSample(x, y, 0);
244: data[k + (i >> 3)] |= v << (7 - (i & 7));
245: }
246: k += w + 7 >> 3;
247: }
248: inputRaster = ras;
249: }
250: }
251:
252: // If the data are not formatted nominally then reformat.
253: if (!destSM.equals(inputRaster.getSampleModel())) {
254: WritableRaster raster = Raster.createWritableRaster(destSM,
255: new Point(inputRaster.getMinX(), inputRaster
256: .getMinY()));
257: raster.setRect(inputRaster);
258: inputRaster = raster;
259: }
260:
261: // Check whether the image is white-is-zero.
262: boolean isWhiteZero = false;
263: if (!writeRaster
264: && input.getColorModel() instanceof IndexColorModel) {
265: IndexColorModel icm = (IndexColorModel) input
266: .getColorModel();
267: isWhiteZero = icm.getRed(0) > icm.getRed(1);
268: }
269:
270: // Get the line stride, bytes per row, and data array.
271: int lineStride = ((MultiPixelPackedSampleModel) destSM)
272: .getScanlineStride();
273: int bytesPerRow = (w + 7) / 8;
274: byte[] bdata = ((DataBufferByte) inputRaster.getDataBuffer())
275: .getData();
276:
277: // Write WBMP header.
278: stream.write(0); // TypeField
279: stream.write(0); // FixHeaderField
280: stream.write(intToMultiByte(w)); // width
281: stream.write(intToMultiByte(h)); // height
282:
283: // Write the data.
284: if (!isWhiteZero && lineStride == bytesPerRow) {
285: // Write the entire image.
286: stream.write(bdata, 0, h * bytesPerRow);
287: processImageProgress(100.0F);
288: } else {
289: // Write the image row-by-row.
290: int offset = 0;
291: if (!isWhiteZero) {
292: // Black-is-zero
293: for (int row = 0; row < h; row++) {
294: if (abortRequested())
295: break;
296: stream.write(bdata, offset, bytesPerRow);
297: offset += lineStride;
298: processImageProgress(100.0F * row / h);
299: }
300: } else {
301: // White-is-zero: need to invert data.
302: byte[] inverted = new byte[bytesPerRow];
303: for (int row = 0; row < h; row++) {
304: if (abortRequested())
305: break;
306: for (int col = 0; col < bytesPerRow; col++) {
307: inverted[col] = (byte) (~(bdata[col + offset]));
308: }
309: stream.write(inverted, 0, bytesPerRow);
310: offset += lineStride;
311: processImageProgress(100.0F * row / h);
312: }
313: }
314: }
315:
316: if (abortRequested())
317: processWriteAborted();
318: else {
319: processImageComplete();
320: stream.flushBefore(stream.getStreamPosition());
321: }
322: }
323:
324: public void reset() {
325: super .reset();
326: stream = null;
327: }
328:
329: private void checkSampleModel(SampleModel sm) {
330: int type = sm.getDataType();
331: if (type < DataBuffer.TYPE_BYTE || type > DataBuffer.TYPE_INT
332: || sm.getNumBands() != 1 || sm.getSampleSize(0) != 1)
333: throw new IllegalArgumentException(I18N
334: .getString("WBMPImageWriter2"));
335: }
336: }
|