001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.swt.internal.image;
011:
012: import org.eclipse.swt.*;
013: import org.eclipse.swt.graphics.*;
014: import java.io.*;
015:
016: final class OS2BMPFileFormat extends FileFormat {
017: static final int BMPFileHeaderSize = 14;
018: static final int BMPHeaderFixedSize = 12;
019: int width, height, bitCount;
020:
021: boolean isFileFormat(LEDataInputStream stream) {
022: try {
023: byte[] header = new byte[18];
024: stream.read(header);
025: stream.unread(header);
026: int infoHeaderSize = (header[14] & 0xFF)
027: | ((header[15] & 0xFF) << 8)
028: | ((header[16] & 0xFF) << 16)
029: | ((header[17] & 0xFF) << 24);
030: return header[0] == 0x42 && header[1] == 0x4D
031: && infoHeaderSize == BMPHeaderFixedSize;
032: } catch (Exception e) {
033: return false;
034: }
035: }
036:
037: byte[] loadData(byte[] infoHeader) {
038: int stride = (width * bitCount + 7) / 8;
039: stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
040: byte[] data = loadData(infoHeader, stride);
041: flipScanLines(data, stride, height);
042: return data;
043: }
044:
045: byte[] loadData(byte[] infoHeader, int stride) {
046: int dataSize = height * stride;
047: byte[] data = new byte[dataSize];
048: try {
049: if (inputStream.read(data) != dataSize)
050: SWT.error(SWT.ERROR_INVALID_IMAGE);
051: } catch (IOException e) {
052: SWT.error(SWT.ERROR_IO, e);
053: }
054: return data;
055: }
056:
057: int[] loadFileHeader() {
058: int[] header = new int[5];
059: try {
060: header[0] = inputStream.readShort();
061: header[1] = inputStream.readInt();
062: header[2] = inputStream.readShort();
063: header[3] = inputStream.readShort();
064: header[4] = inputStream.readInt();
065: } catch (IOException e) {
066: SWT.error(SWT.ERROR_IO, e);
067: }
068: if (header[0] != 0x4D42)
069: SWT.error(SWT.ERROR_INVALID_IMAGE);
070: return header;
071: }
072:
073: ImageData[] loadFromByteStream() {
074: int[] fileHeader = loadFileHeader();
075: byte[] infoHeader = new byte[BMPHeaderFixedSize];
076: try {
077: inputStream.read(infoHeader);
078: } catch (Exception e) {
079: SWT.error(SWT.ERROR_IO, e);
080: }
081: width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8);
082: height = (infoHeader[6] & 0xFF) | ((infoHeader[7] & 0xFF) << 8);
083: bitCount = (infoHeader[10] & 0xFF)
084: | ((infoHeader[11] & 0xFF) << 8);
085: PaletteData palette = loadPalette(infoHeader);
086: if (inputStream.getPosition() < fileHeader[4]) {
087: // Seek to the specified offset
088: try {
089: inputStream.skip(fileHeader[4]
090: - inputStream.getPosition());
091: } catch (IOException e) {
092: SWT.error(SWT.ERROR_IO, e);
093: }
094: }
095: byte[] data = loadData(infoHeader);
096: int type = SWT.IMAGE_OS2_BMP;
097: return new ImageData[] { ImageData.internal_new(width, height,
098: bitCount, palette, 4, data, 0, null, null, -1, -1,
099: type, 0, 0, 0, 0) };
100: }
101:
102: PaletteData loadPalette(byte[] infoHeader) {
103: if (bitCount <= 8) {
104: int numColors = 1 << bitCount;
105: byte[] buf = new byte[numColors * 3];
106: try {
107: if (inputStream.read(buf) != buf.length)
108: SWT.error(SWT.ERROR_INVALID_IMAGE);
109: } catch (IOException e) {
110: SWT.error(SWT.ERROR_IO, e);
111: }
112: return paletteFromBytes(buf, numColors);
113: }
114: if (bitCount == 16)
115: return new PaletteData(0x7C00, 0x3E0, 0x1F);
116: if (bitCount == 24)
117: return new PaletteData(0xFF, 0xFF00, 0xFF0000);
118: return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
119: }
120:
121: PaletteData paletteFromBytes(byte[] bytes, int numColors) {
122: int bytesOffset = 0;
123: RGB[] colors = new RGB[numColors];
124: for (int i = 0; i < numColors; i++) {
125: colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
126: bytes[bytesOffset + 1] & 0xFF,
127: bytes[bytesOffset] & 0xFF);
128: bytesOffset += 3;
129: }
130: return new PaletteData(colors);
131: }
132:
133: /**
134: * Answer a byte array containing the BMP representation of
135: * the given device independent palette.
136: */
137: static byte[] paletteToBytes(PaletteData pal) {
138: int n = pal.colors == null ? 0
139: : (pal.colors.length < 256 ? pal.colors.length : 256);
140: byte[] bytes = new byte[n * 3];
141: int offset = 0;
142: for (int i = 0; i < n; i++) {
143: RGB col = pal.colors[i];
144: bytes[offset] = (byte) col.blue;
145: bytes[offset + 1] = (byte) col.green;
146: bytes[offset + 2] = (byte) col.red;
147: offset += 3;
148: }
149: return bytes;
150: }
151:
152: /**
153: * Unload the given image's data into the given byte stream.
154: * Answer the number of bytes written.
155: */
156: int unloadData(ImageData image, OutputStream out) {
157: int bmpBpl = 0;
158: try {
159: int bpl = (image.width * image.depth + 7) / 8;
160: bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
161: int linesPerBuf = 32678 / bmpBpl;
162: byte[] buf = new byte[linesPerBuf * bmpBpl];
163: byte[] data = image.data;
164: int imageBpl = image.bytesPerLine;
165: int dataIndex = imageBpl * (image.height - 1); // Start at last line
166: if (image.depth == 16) {
167: for (int y = 0; y < image.height; y += linesPerBuf) {
168: int count = image.height - y;
169: if (linesPerBuf < count)
170: count = linesPerBuf;
171: int bufOffset = 0;
172: for (int i = 0; i < count; i++) {
173: for (int wIndex = 0; wIndex < bpl; wIndex += 2) {
174: buf[bufOffset + wIndex + 1] = data[dataIndex
175: + wIndex + 1];
176: buf[bufOffset + wIndex] = data[dataIndex
177: + wIndex];
178: }
179: bufOffset += bmpBpl;
180: dataIndex -= imageBpl;
181: }
182: out.write(buf, 0, bufOffset);
183: }
184: } else {
185: for (int y = 0; y < image.height; y += linesPerBuf) {
186: int tmp = image.height - y;
187: int count = tmp < linesPerBuf ? tmp : linesPerBuf;
188: int bufOffset = 0;
189: for (int i = 0; i < count; i++) {
190: System.arraycopy(data, dataIndex, buf,
191: bufOffset, bpl);
192: bufOffset += bmpBpl;
193: dataIndex -= imageBpl;
194: }
195: out.write(buf, 0, bufOffset);
196: }
197: }
198: } catch (IOException e) {
199: SWT.error(SWT.ERROR_IO, e);
200: }
201: return bmpBpl * image.height;
202: }
203:
204: /**
205: * Unload a DeviceIndependentImage using Windows .BMP format into the given
206: * byte stream.
207: */
208: void unloadIntoByteStream(ImageLoader loader) {
209: ImageData image = loader.data[0];
210: byte[] rgbs;
211: int numCols;
212: if (!((image.depth == 1) || (image.depth == 4)
213: || (image.depth == 8) || (image.depth == 16)
214: || (image.depth == 24) || (image.depth == 32)))
215: SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
216: PaletteData pal = image.palette;
217: if ((image.depth == 16) || (image.depth == 24)
218: || (image.depth == 32)) {
219: if (!pal.isDirect)
220: SWT.error(SWT.ERROR_INVALID_IMAGE);
221: numCols = 0;
222: rgbs = null;
223: } else {
224: if (pal.isDirect)
225: SWT.error(SWT.ERROR_INVALID_IMAGE);
226: numCols = pal.colors.length;
227: rgbs = paletteToBytes(pal);
228: }
229: // Fill in file header, except for bfsize, which is done later.
230: int headersSize = BMPFileHeaderSize + BMPHeaderFixedSize;
231: int[] fileHeader = new int[5];
232: fileHeader[0] = 0x4D42; // Signature
233: fileHeader[1] = 0; // File size - filled in later
234: fileHeader[2] = 0; // Reserved 1
235: fileHeader[3] = 0; // Reserved 2
236: fileHeader[4] = headersSize; // Offset to data
237: if (rgbs != null) {
238: fileHeader[4] += rgbs.length;
239: }
240:
241: // Prepare data. This is done first so we don't have to try to rewind
242: // the stream and fill in the details later.
243: ByteArrayOutputStream out = new ByteArrayOutputStream();
244: unloadData(image, out);
245: byte[] data = out.toByteArray();
246:
247: // Calculate file size
248: fileHeader[1] = fileHeader[4] + data.length;
249:
250: // Write the headers
251: try {
252: outputStream.writeShort(fileHeader[0]);
253: outputStream.writeInt(fileHeader[1]);
254: outputStream.writeShort(fileHeader[2]);
255: outputStream.writeShort(fileHeader[3]);
256: outputStream.writeInt(fileHeader[4]);
257: } catch (IOException e) {
258: SWT.error(SWT.ERROR_IO, e);
259: }
260: try {
261: outputStream.writeInt(BMPHeaderFixedSize);
262: outputStream.writeShort(image.width);
263: outputStream.writeShort(image.height);
264: outputStream.writeShort(1);
265: outputStream.writeShort((short) image.depth);
266: } catch (IOException e) {
267: SWT.error(SWT.ERROR_IO, e);
268: }
269:
270: // Unload palette
271: if (numCols > 0) {
272: try {
273: outputStream.write(rgbs);
274: } catch (IOException e) {
275: SWT.error(SWT.ERROR_IO, e);
276: }
277: }
278:
279: // Unload the data
280: try {
281: outputStream.write(data);
282: } catch (IOException e) {
283: SWT.error(SWT.ERROR_IO, e);
284: }
285: }
286:
287: void flipScanLines(byte[] data, int stride, int height) {
288: int i1 = 0;
289: int i2 = (height - 1) * stride;
290: for (int i = 0; i < height / 2; i++) {
291: for (int index = 0; index < stride; index++) {
292: byte b = data[index + i1];
293: data[index + i1] = data[index + i2];
294: data[index + i2] = b;
295: }
296: i1 += stride;
297: i2 -= stride;
298: }
299: }
300:
301: }
|