comparison dwt/internal/image/OS2BMPFileFormat.d @ 13:3d9bbe0a83a0

FileFormats
author Frank Benoit <benoit@tionex.de>
date Sun, 06 Jan 2008 22:54:14 +0100
parents
children fc2b263b8a3f
comparison
equal deleted inserted replaced
12:0c78fa47d476 13:3d9bbe0a83a0
1 /*******************************************************************************
2 * Copyright (c) 2000, 2005 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 module dwt.internal.image.OS2BMPFileFormat;
12
13 import dwt.SWT;
14 import dwt.graphics.ImageData;
15 import dwt.graphics.ImageLoader;
16 import dwt.graphics.PaletteData;
17 import dwt.graphics.RGB;
18 import dwt.internal.image.LEDataInputStream;
19 import dwt.internal.image.FileFormat;
20 import dwt.dwthelper.ByteArrayOutputStream;
21
22 import tango.core.Exception;
23
24 final class OS2BMPFileFormat : FileFormat {
25 static final int BMPFileHeaderSize = 14;
26 static final int BMPHeaderFixedSize = 12;
27 int width, height, bitCount;
28
29 bool isFileFormat(LEDataInputStream stream) {
30 try {
31 byte[] header = new byte[18];
32 stream.read(header);
33 stream.unread(header);
34 int infoHeaderSize = (header[14] & 0xFF) | ((header[15] & 0xFF) << 8) | ((header[16] & 0xFF) << 16) | ((header[17] & 0xFF) << 24);
35 return header[0] == 0x42 && header[1] == 0x4D && infoHeaderSize == BMPHeaderFixedSize;
36 } catch (TracedException e) {
37 return false;
38 }
39 }
40 byte[] loadData(byte[] infoHeader) {
41 int stride = (width * bitCount + 7) / 8;
42 stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
43 byte[] data = loadData(infoHeader, stride);
44 flipScanLines(data, stride, height);
45 return data;
46 }
47 byte[] loadData(byte[] infoHeader, int stride) {
48 int dataSize = height * stride;
49 byte[] data = new byte[dataSize];
50 try {
51 if (inputStream.read(data) != dataSize)
52 SWT.error(SWT.ERROR_INVALID_IMAGE);
53 } catch (IOException e) {
54 SWT.error(SWT.ERROR_IO, e);
55 }
56 return data;
57 }
58 int[] loadFileHeader() {
59 int[] header = new int[5];
60 try {
61 header[0] = inputStream.readShort();
62 header[1] = inputStream.readInt();
63 header[2] = inputStream.readShort();
64 header[3] = inputStream.readShort();
65 header[4] = inputStream.readInt();
66 } catch (IOException e) {
67 SWT.error(SWT.ERROR_IO, e);
68 }
69 if (header[0] != 0x4D42)
70 SWT.error(SWT.ERROR_INVALID_IMAGE);
71 return header;
72 }
73 ImageData[] loadFromByteStream() {
74 int[] fileHeader = loadFileHeader();
75 byte[] infoHeader = new byte[BMPHeaderFixedSize];
76 try {
77 inputStream.read(infoHeader);
78 } catch (TracedException e) {
79 SWT.error(SWT.ERROR_IO, e);
80 }
81 width = (infoHeader[4] & 0xFF) | ((infoHeader[5] & 0xFF) << 8);
82 height = (infoHeader[6] & 0xFF) | ((infoHeader[7] & 0xFF) << 8);
83 bitCount = (infoHeader[10] & 0xFF) | ((infoHeader[11] & 0xFF) << 8);
84 PaletteData palette = loadPalette(infoHeader);
85 if (inputStream.getPosition() < fileHeader[4]) {
86 // Seek to the specified offset
87 try {
88 inputStream.skip(fileHeader[4] - inputStream.getPosition());
89 } catch (IOException e) {
90 SWT.error(SWT.ERROR_IO, e);
91 }
92 }
93 byte[] data = loadData(infoHeader);
94 int type = SWT.IMAGE_OS2_BMP;
95 return [
96 ImageData.internal_new(
97 width,
98 height,
99 bitCount,
100 palette,
101 4,
102 data,
103 0,
104 null,
105 null,
106 -1,
107 -1,
108 type,
109 0,
110 0,
111 0,
112 0)
113 ];
114 }
115 PaletteData loadPalette(byte[] infoHeader) {
116 if (bitCount <= 8) {
117 int numColors = 1 << bitCount;
118 byte[] buf = new byte[numColors * 3];
119 try {
120 if (inputStream.read(buf) != buf.length)
121 SWT.error(SWT.ERROR_INVALID_IMAGE);
122 } catch (IOException e) {
123 SWT.error(SWT.ERROR_IO, e);
124 }
125 return paletteFromBytes(buf, numColors);
126 }
127 if (bitCount == 16) return new PaletteData(0x7C00, 0x3E0, 0x1F);
128 if (bitCount == 24) return new PaletteData(0xFF, 0xFF00, 0xFF0000);
129 return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
130 }
131 PaletteData paletteFromBytes(byte[] bytes, int numColors) {
132 int bytesOffset = 0;
133 RGB[] colors = new RGB[numColors];
134 for (int i = 0; i < numColors; i++) {
135 colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
136 bytes[bytesOffset + 1] & 0xFF,
137 bytes[bytesOffset] & 0xFF);
138 bytesOffset += 3;
139 }
140 return new PaletteData(colors);
141 }
142 /**
143 * Answer a byte array containing the BMP representation of
144 * the given device independent palette.
145 */
146 static byte[] paletteToBytes(PaletteData pal) {
147 int n = pal.colors == null ? 0 : (pal.colors.length < 256 ? pal.colors.length : 256);
148 byte[] bytes = new byte[n * 3];
149 int offset = 0;
150 for (int i = 0; i < n; i++) {
151 RGB col = pal.colors[i];
152 bytes[offset] = cast(byte)col.blue;
153 bytes[offset + 1] = cast(byte)col.green;
154 bytes[offset + 2] = cast(byte)col.red;
155 offset += 3;
156 }
157 return bytes;
158 }
159 /**
160 * Unload the given image's data into the given byte stream.
161 * Answer the number of bytes written.
162 */
163 int unloadData(ImageData image, OutputStream ostr) {
164 int bmpBpl = 0;
165 try {
166 int bpl = (image.width * image.depth + 7) / 8;
167 bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
168 int linesPerBuf = 32678 / bmpBpl;
169 byte[] buf = new byte[linesPerBuf * bmpBpl];
170 byte[] data = image.data;
171 int imageBpl = image.bytesPerLine;
172 int dataIndex = imageBpl * (image.height - 1); // Start at last line
173 if (image.depth == 16) {
174 for (int y = 0; y < image.height; y += linesPerBuf) {
175 int count = image.height - y;
176 if (linesPerBuf < count) count = linesPerBuf;
177 int bufOffset = 0;
178 for (int i = 0; i < count; i++) {
179 for (int wIndex = 0; wIndex < bpl; wIndex += 2) {
180 buf[bufOffset + wIndex + 1] = data[dataIndex + wIndex + 1];
181 buf[bufOffset + wIndex] = data[dataIndex + wIndex];
182 }
183 bufOffset += bmpBpl;
184 dataIndex -= imageBpl;
185 }
186 ostr.write(buf, 0, bufOffset);
187 }
188 } else {
189 for (int y = 0; y < image.height; y += linesPerBuf) {
190 int tmp = image.height - y;
191 int count = tmp < linesPerBuf ? tmp : linesPerBuf;
192 int bufOffset = 0;
193 for (int i = 0; i < count; i++) {
194 buf[ bufOffset .. bufOffset+bpl ] = data[ dataIndex .. dataIndex+bpl ];
195 bufOffset += bmpBpl;
196 dataIndex -= imageBpl;
197 }
198 ostr.write(buf, 0, bufOffset);
199 }
200 }
201 } catch (IOException e) {
202 SWT.error(SWT.ERROR_IO, e);
203 }
204 return bmpBpl * image.height;
205 }
206 /**
207 * Unload a DeviceIndependentImage using Windows .BMP format into the given
208 * byte stream.
209 */
210 void unloadIntoByteStream(ImageLoader loader) {
211 ImageData image = loader.data[0];
212 byte[] rgbs;
213 int numCols;
214 if (!((image.depth == 1) || (image.depth == 4) || (image.depth == 8) ||
215 (image.depth == 16) || (image.depth == 24) || (image.depth == 32)))
216 SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
217 PaletteData pal = image.palette;
218 if ((image.depth == 16) || (image.depth == 24) || (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 ostr = new ByteArrayOutputStream();
244 unloadData(image, ostr);
245 byte[] data = ostr.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(cast(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 void flipScanLines(byte[] data, int stride, int height) {
287 int i1 = 0;
288 int i2 = (height - 1) * stride;
289 for (int i = 0; i < height / 2; i++) {
290 for (int index = 0; index < stride; index++) {
291 byte b = data[index + i1];
292 data[index + i1] = data[index + i2];
293 data[index + i2] = b;
294 }
295 i1 += stride;
296 i2 -= stride;
297 }
298 }
299
300 }