001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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 WinBMPFileFormat extends FileFormat {
017: static final int BMPFileHeaderSize = 14;
018: static final int BMPHeaderFixedSize = 40;
019: int importantColors;
020: Point pelsPerMeter = new Point(0, 0);
021:
022: /**
023: * Compress numBytes bytes of image data from src, storing in dest
024: * (starting at 0), using the technique specified by comp.
025: * If last is true, this indicates the last line of the image.
026: * Answer the size of the compressed data.
027: */
028: int compress(int comp, byte[] src, int srcOffset, int numBytes,
029: byte[] dest, boolean last) {
030: if (comp == 1) { // BMP_RLE8_COMPRESSION
031: return compressRLE8Data(src, srcOffset, numBytes, dest,
032: last);
033: }
034: if (comp == 2) { // BMP_RLE4_COMPRESSION
035: return compressRLE4Data(src, srcOffset, numBytes, dest,
036: last);
037: }
038: SWT.error(SWT.ERROR_INVALID_IMAGE);
039: return 0;
040: }
041:
042: int compressRLE4Data(byte[] src, int srcOffset, int numBytes,
043: byte[] dest, boolean last) {
044: int sp = srcOffset, end = srcOffset + numBytes, dp = 0;
045: int size = 0, left, i, n;
046: byte theByte;
047: while (sp < end) {
048: /* find two consecutive bytes that are the same in the next 128 */
049: left = end - sp - 1;
050: if (left > 127)
051: left = 127;
052: for (n = 0; n < left; n++) {
053: if (src[sp + n] == src[sp + n + 1])
054: break;
055: }
056: /* if there is only one more byte in the scan line, include it */
057: if (n < 127 && n == left)
058: n++;
059: /* store the intervening data */
060: switch (n) {
061: case 0:
062: break;
063: case 1: /* handled separately because 0,2 is a command */
064: dest[dp] = 2;
065: dp++; /* 1 byte == 2 pixels */
066: dest[dp] = src[sp];
067: dp++;
068: sp++;
069: size += 2;
070: break;
071: default:
072: dest[dp] = 0;
073: dp++;
074: dest[dp] = (byte) (n + n);
075: dp++; /* n bytes = n*2 pixels */
076: for (i = n; i > 0; i--) {
077: dest[dp] = src[sp];
078: dp++;
079: sp++;
080: }
081: size += 2 + n;
082: if ((n & 1) != 0) { /* pad to word */
083: dest[dp] = 0;
084: dp++;
085: size++;
086: }
087: break;
088: }
089: /* find the length of the next run (up to 127) and store it */
090: left = end - sp;
091: if (left > 0) {
092: if (left > 127)
093: left = 127;
094: theByte = src[sp];
095: for (n = 1; n < left; n++) {
096: if (src[sp + n] != theByte)
097: break;
098: }
099: dest[dp] = (byte) (n + n);
100: dp++; /* n bytes = n*2 pixels */
101: dest[dp] = theByte;
102: dp++;
103: sp += n;
104: size += 2;
105: }
106: }
107:
108: /* store the end of line or end of bitmap codes */
109: dest[dp] = 0;
110: dp++;
111: if (last) {
112: dest[dp] = 1;
113: dp++;
114: } else {
115: dest[dp] = 0;
116: dp++;
117: }
118: size += 2;
119:
120: return size;
121: }
122:
123: int compressRLE8Data(byte[] src, int srcOffset, int numBytes,
124: byte[] dest, boolean last) {
125: int sp = srcOffset, end = srcOffset + numBytes, dp = 0;
126: int size = 0, left, i, n;
127: byte theByte;
128: while (sp < end) {
129: /* find two consecutive bytes that are the same in the next 256 */
130: left = end - sp - 1;
131: if (left > 254)
132: left = 254;
133: for (n = 0; n < left; n++) {
134: if (src[sp + n] == src[sp + n + 1])
135: break;
136: }
137: /* if there is only one more byte in the scan line, include it */
138: if (n == left)
139: n++;
140: /* store the intervening data */
141: switch (n) {
142: case 0:
143: break;
144: case 2: /* handled separately because 0,2 is a command */
145: dest[dp] = 1;
146: dp++;
147: dest[dp] = src[sp];
148: dp++;
149: sp++;
150: size += 2;
151: /* don't break, fall through */
152: case 1: /* handled separately because 0,1 is a command */
153: dest[dp] = 1;
154: dp++;
155: dest[dp] = src[sp];
156: dp++;
157: sp++;
158: size += 2;
159: break;
160: default:
161: dest[dp] = 0;
162: dp++;
163: dest[dp] = (byte) n;
164: dp++;
165: for (i = n; i > 0; i--) {
166: dest[dp] = src[sp];
167: dp++;
168: sp++;
169: }
170: size += 2 + n;
171: if ((n & 1) != 0) { /* pad to word */
172: dest[dp] = 0;
173: dp++;
174: size++;
175: }
176: break;
177: }
178: /* find the length of the next run (up to 255) and store it */
179: left = end - sp;
180: if (left > 0) {
181: if (left > 255)
182: left = 255;
183: theByte = src[sp];
184: for (n = 1; n < left; n++) {
185: if (src[sp + n] != theByte)
186: break;
187: }
188: dest[dp] = (byte) n;
189: dp++;
190: dest[dp] = theByte;
191: dp++;
192: sp += n;
193: size += 2;
194: }
195: }
196:
197: /* store the end of line or end of bitmap codes */
198: dest[dp] = 0;
199: dp++;
200: if (last) {
201: dest[dp] = 1;
202: dp++;
203: } else {
204: dest[dp] = 0;
205: dp++;
206: }
207: size += 2;
208:
209: return size;
210: }
211:
212: void decompressData(byte[] src, byte[] dest, int stride, int cmp) {
213: if (cmp == 1) { // BMP_RLE8_COMPRESSION
214: if (decompressRLE8Data(src, src.length, stride, dest,
215: dest.length) <= 0)
216: SWT.error(SWT.ERROR_INVALID_IMAGE);
217: return;
218: }
219: if (cmp == 2) { // BMP_RLE4_COMPRESSION
220: if (decompressRLE4Data(src, src.length, stride, dest,
221: dest.length) <= 0)
222: SWT.error(SWT.ERROR_INVALID_IMAGE);
223: return;
224: }
225: SWT.error(SWT.ERROR_INVALID_IMAGE);
226: }
227:
228: int decompressRLE4Data(byte[] src, int numBytes, int stride,
229: byte[] dest, int destSize) {
230: int sp = 0;
231: int se = numBytes;
232: int dp = 0;
233: int de = destSize;
234: int x = 0, y = 0;
235: while (sp < se) {
236: int len = src[sp] & 0xFF;
237: sp++;
238: if (len == 0) {
239: len = src[sp] & 0xFF;
240: sp++;
241: switch (len) {
242: case 0: /* end of line */
243: y++;
244: x = 0;
245: dp = y * stride;
246: if (dp > de)
247: return -1;
248: break;
249: case 1: /* end of bitmap */
250: return 1;
251: case 2: /* delta */
252: x += src[sp] & 0xFF;
253: sp++;
254: y += src[sp] & 0xFF;
255: sp++;
256: dp = y * stride + x / 2;
257: if (dp > de)
258: return -1;
259: break;
260: default: /* absolute mode run */
261: if ((len & 1) != 0) /* odd run lengths not currently supported */
262: return -1;
263: x += len;
264: len = len / 2;
265: if (len > (se - sp))
266: return -1;
267: if (len > (de - dp))
268: return -1;
269: for (int i = 0; i < len; i++) {
270: dest[dp] = src[sp];
271: dp++;
272: sp++;
273: }
274: if ((sp & 1) != 0)
275: sp++; /* word align sp? */
276: break;
277: }
278: } else {
279: if ((len & 1) != 0)
280: return -1;
281: x += len;
282: len = len / 2;
283: byte theByte = src[sp];
284: sp++;
285: if (len > (de - dp))
286: return -1;
287: for (int i = 0; i < len; i++) {
288: dest[dp] = theByte;
289: dp++;
290: }
291: }
292: }
293: return 1;
294: }
295:
296: int decompressRLE8Data(byte[] src, int numBytes, int stride,
297: byte[] dest, int destSize) {
298: int sp = 0;
299: int se = numBytes;
300: int dp = 0;
301: int de = destSize;
302: int x = 0, y = 0;
303: while (sp < se) {
304: int len = src[sp] & 0xFF;
305: sp++;
306: if (len == 0) {
307: len = src[sp] & 0xFF;
308: sp++;
309: switch (len) {
310: case 0: /* end of line */
311: y++;
312: x = 0;
313: dp = y * stride;
314: if (dp > de)
315: return -1;
316: break;
317: case 1: /* end of bitmap */
318: return 1;
319: case 2: /* delta */
320: x += src[sp] & 0xFF;
321: sp++;
322: y += src[sp] & 0xFF;
323: sp++;
324: dp = y * stride + x;
325: if (dp > de)
326: return -1;
327: break;
328: default: /* absolute mode run */
329: if (len > (se - sp))
330: return -1;
331: if (len > (de - dp))
332: return -1;
333: for (int i = 0; i < len; i++) {
334: dest[dp] = src[sp];
335: dp++;
336: sp++;
337: }
338: if ((sp & 1) != 0)
339: sp++; /* word align sp? */
340: x += len;
341: break;
342: }
343: } else {
344: byte theByte = src[sp];
345: sp++;
346: if (len > (de - dp))
347: return -1;
348: for (int i = 0; i < len; i++) {
349: dest[dp] = theByte;
350: dp++;
351: }
352: x += len;
353: }
354: }
355: return 1;
356: }
357:
358: boolean isFileFormat(LEDataInputStream stream) {
359: try {
360: byte[] header = new byte[18];
361: stream.read(header);
362: stream.unread(header);
363: int infoHeaderSize = (header[14] & 0xFF)
364: | ((header[15] & 0xFF) << 8)
365: | ((header[16] & 0xFF) << 16)
366: | ((header[17] & 0xFF) << 24);
367: return header[0] == 0x42 && header[1] == 0x4D
368: && infoHeaderSize >= BMPHeaderFixedSize;
369: } catch (Exception e) {
370: return false;
371: }
372: }
373:
374: byte[] loadData(byte[] infoHeader) {
375: int width = (infoHeader[4] & 0xFF)
376: | ((infoHeader[5] & 0xFF) << 8)
377: | ((infoHeader[6] & 0xFF) << 16)
378: | ((infoHeader[7] & 0xFF) << 24);
379: int height = (infoHeader[8] & 0xFF)
380: | ((infoHeader[9] & 0xFF) << 8)
381: | ((infoHeader[10] & 0xFF) << 16)
382: | ((infoHeader[11] & 0xFF) << 24);
383: int bitCount = (infoHeader[14] & 0xFF)
384: | ((infoHeader[15] & 0xFF) << 8);
385: int stride = (width * bitCount + 7) / 8;
386: stride = (stride + 3) / 4 * 4; // Round up to 4 byte multiple
387: byte[] data = loadData(infoHeader, stride);
388: flipScanLines(data, stride, height);
389: return data;
390: }
391:
392: byte[] loadData(byte[] infoHeader, int stride) {
393: int height = (infoHeader[8] & 0xFF)
394: | ((infoHeader[9] & 0xFF) << 8)
395: | ((infoHeader[10] & 0xFF) << 16)
396: | ((infoHeader[11] & 0xFF) << 24);
397: if (height < 0)
398: height = -height;
399: int dataSize = height * stride;
400: byte[] data = new byte[dataSize];
401: int cmp = (infoHeader[16] & 0xFF)
402: | ((infoHeader[17] & 0xFF) << 8)
403: | ((infoHeader[18] & 0xFF) << 16)
404: | ((infoHeader[19] & 0xFF) << 24);
405: if (cmp == 0 || cmp == 3) { // BMP_NO_COMPRESSION
406: try {
407: if (inputStream.read(data) != dataSize)
408: SWT.error(SWT.ERROR_INVALID_IMAGE);
409: } catch (IOException e) {
410: SWT.error(SWT.ERROR_IO, e);
411: }
412: } else {
413: int compressedSize = (infoHeader[20] & 0xFF)
414: | ((infoHeader[21] & 0xFF) << 8)
415: | ((infoHeader[22] & 0xFF) << 16)
416: | ((infoHeader[23] & 0xFF) << 24);
417: byte[] compressed = new byte[compressedSize];
418: try {
419: if (inputStream.read(compressed) != compressedSize)
420: SWT.error(SWT.ERROR_INVALID_IMAGE);
421: } catch (IOException e) {
422: SWT.error(SWT.ERROR_IO, e);
423: }
424: decompressData(compressed, data, stride, cmp);
425: }
426: return data;
427: }
428:
429: int[] loadFileHeader() {
430: int[] header = new int[5];
431: try {
432: header[0] = inputStream.readShort();
433: header[1] = inputStream.readInt();
434: header[2] = inputStream.readShort();
435: header[3] = inputStream.readShort();
436: header[4] = inputStream.readInt();
437: } catch (IOException e) {
438: SWT.error(SWT.ERROR_IO, e);
439: }
440: if (header[0] != 0x4D42)
441: SWT.error(SWT.ERROR_INVALID_IMAGE);
442: return header;
443: }
444:
445: ImageData[] loadFromByteStream() {
446: int[] fileHeader = loadFileHeader();
447: byte[] infoHeader = new byte[BMPHeaderFixedSize];
448: try {
449: inputStream.read(infoHeader);
450: } catch (Exception e) {
451: SWT.error(SWT.ERROR_IO, e);
452: }
453: int width = (infoHeader[4] & 0xFF)
454: | ((infoHeader[5] & 0xFF) << 8)
455: | ((infoHeader[6] & 0xFF) << 16)
456: | ((infoHeader[7] & 0xFF) << 24);
457: int height = (infoHeader[8] & 0xFF)
458: | ((infoHeader[9] & 0xFF) << 8)
459: | ((infoHeader[10] & 0xFF) << 16)
460: | ((infoHeader[11] & 0xFF) << 24);
461: if (height < 0)
462: height = -height;
463: int bitCount = (infoHeader[14] & 0xFF)
464: | ((infoHeader[15] & 0xFF) << 8);
465: this .compression = (infoHeader[16] & 0xFF)
466: | ((infoHeader[17] & 0xFF) << 8)
467: | ((infoHeader[18] & 0xFF) << 16)
468: | ((infoHeader[19] & 0xFF) << 24);
469: PaletteData palette = loadPalette(infoHeader);
470: if (inputStream.getPosition() < fileHeader[4]) {
471: // Seek to the specified offset
472: try {
473: inputStream.skip(fileHeader[4]
474: - inputStream.getPosition());
475: } catch (IOException e) {
476: SWT.error(SWT.ERROR_IO, e);
477: }
478: }
479: byte[] data = loadData(infoHeader);
480: this .importantColors = (infoHeader[36] & 0xFF)
481: | ((infoHeader[37] & 0xFF) << 8)
482: | ((infoHeader[38] & 0xFF) << 16)
483: | ((infoHeader[39] & 0xFF) << 24);
484: int xPelsPerMeter = (infoHeader[24] & 0xFF)
485: | ((infoHeader[25] & 0xFF) << 8)
486: | ((infoHeader[26] & 0xFF) << 16)
487: | ((infoHeader[27] & 0xFF) << 24);
488: int yPelsPerMeter = (infoHeader[28] & 0xFF)
489: | ((infoHeader[29] & 0xFF) << 8)
490: | ((infoHeader[30] & 0xFF) << 16)
491: | ((infoHeader[31] & 0xFF) << 24);
492: this .pelsPerMeter = new Point(xPelsPerMeter, yPelsPerMeter);
493: int type = (this .compression == 1 /*BMP_RLE8_COMPRESSION*/)
494: || (this .compression == 2 /*BMP_RLE4_COMPRESSION*/) ? SWT.IMAGE_BMP_RLE
495: : SWT.IMAGE_BMP;
496: return new ImageData[] { ImageData.internal_new(width, height,
497: bitCount, palette, 4, data, 0, null, null, -1, -1,
498: type, 0, 0, 0, 0) };
499: }
500:
501: PaletteData loadPalette(byte[] infoHeader) {
502: int depth = (infoHeader[14] & 0xFF)
503: | ((infoHeader[15] & 0xFF) << 8);
504: if (depth <= 8) {
505: int numColors = (infoHeader[32] & 0xFF)
506: | ((infoHeader[33] & 0xFF) << 8)
507: | ((infoHeader[34] & 0xFF) << 16)
508: | ((infoHeader[35] & 0xFF) << 24);
509: if (numColors == 0) {
510: numColors = 1 << depth;
511: } else {
512: if (numColors > 256)
513: numColors = 256;
514: }
515: byte[] buf = new byte[numColors * 4];
516: try {
517: if (inputStream.read(buf) != buf.length)
518: SWT.error(SWT.ERROR_INVALID_IMAGE);
519: } catch (IOException e) {
520: SWT.error(SWT.ERROR_IO, e);
521: }
522: return paletteFromBytes(buf, numColors);
523: }
524: if (depth == 16) {
525: if (this .compression == 3) {
526: try {
527: return new PaletteData(inputStream.readInt(),
528: inputStream.readInt(), inputStream
529: .readInt());
530: } catch (IOException e) {
531: SWT.error(SWT.ERROR_IO, e);
532: }
533: }
534: return new PaletteData(0x7C00, 0x3E0, 0x1F);
535: }
536: if (depth == 24)
537: return new PaletteData(0xFF, 0xFF00, 0xFF0000);
538: if (this .compression == 3) {
539: try {
540: return new PaletteData(inputStream.readInt(),
541: inputStream.readInt(), inputStream.readInt());
542: } catch (IOException e) {
543: SWT.error(SWT.ERROR_IO, e);
544: }
545: }
546: return new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
547: }
548:
549: PaletteData paletteFromBytes(byte[] bytes, int numColors) {
550: int bytesOffset = 0;
551: RGB[] colors = new RGB[numColors];
552: for (int i = 0; i < numColors; i++) {
553: colors[i] = new RGB(bytes[bytesOffset + 2] & 0xFF,
554: bytes[bytesOffset + 1] & 0xFF,
555: bytes[bytesOffset] & 0xFF);
556: bytesOffset += 4;
557: }
558: return new PaletteData(colors);
559: }
560:
561: /**
562: * Answer a byte array containing the BMP representation of
563: * the given device independent palette.
564: */
565: static byte[] paletteToBytes(PaletteData pal) {
566: int n = pal.colors == null ? 0
567: : (pal.colors.length < 256 ? pal.colors.length : 256);
568: byte[] bytes = new byte[n * 4];
569: int offset = 0;
570: for (int i = 0; i < n; i++) {
571: RGB col = pal.colors[i];
572: bytes[offset] = (byte) col.blue;
573: bytes[offset + 1] = (byte) col.green;
574: bytes[offset + 2] = (byte) col.red;
575: offset += 4;
576: }
577: return bytes;
578: }
579:
580: /**
581: * Unload the given image's data into the given byte stream
582: * using the given compression strategy.
583: * Answer the number of bytes written.
584: */
585: int unloadData(ImageData image, OutputStream out, int comp) {
586: int totalSize = 0;
587: try {
588: if (comp == 0)
589: return unloadDataNoCompression(image, out);
590: int bpl = (image.width * image.depth + 7) / 8;
591: int bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
592: int imageBpl = image.bytesPerLine;
593: // Compression can actually take twice as much space, in worst case
594: byte[] buf = new byte[bmpBpl * 2];
595: int srcOffset = imageBpl * (image.height - 1); // Start at last line
596: byte[] data = image.data;
597: totalSize = 0;
598: byte[] buf2 = new byte[32768];
599: int buf2Offset = 0;
600: for (int y = image.height - 1; y >= 0; y--) {
601: int lineSize = compress(comp, data, srcOffset, bpl,
602: buf, y == 0);
603: if (buf2Offset + lineSize > buf2.length) {
604: out.write(buf2, 0, buf2Offset);
605: buf2Offset = 0;
606: }
607: System.arraycopy(buf, 0, buf2, buf2Offset, lineSize);
608: buf2Offset += lineSize;
609: totalSize += lineSize;
610: srcOffset -= imageBpl;
611: }
612: if (buf2Offset > 0)
613: out.write(buf2, 0, buf2Offset);
614: } catch (IOException e) {
615: SWT.error(SWT.ERROR_IO, e);
616: }
617: return totalSize;
618: }
619:
620: /**
621: * Prepare the given image's data for unloading into a byte stream
622: * using no compression strategy.
623: * Answer the number of bytes written.
624: */
625: int unloadDataNoCompression(ImageData image, OutputStream out) {
626: int bmpBpl = 0;
627: try {
628: int bpl = (image.width * image.depth + 7) / 8;
629: bmpBpl = (bpl + 3) / 4 * 4; // BMP pads scanlines to multiples of 4 bytes
630: int linesPerBuf = 32678 / bmpBpl;
631: byte[] buf = new byte[linesPerBuf * bmpBpl];
632: byte[] data = image.data;
633: int imageBpl = image.bytesPerLine;
634: int dataIndex = imageBpl * (image.height - 1); // Start at last line
635: if (image.depth == 16) {
636: for (int y = 0; y < image.height; y += linesPerBuf) {
637: int count = image.height - y;
638: if (linesPerBuf < count)
639: count = linesPerBuf;
640: int bufOffset = 0;
641: for (int i = 0; i < count; i++) {
642: for (int wIndex = 0; wIndex < bpl; wIndex += 2) {
643: buf[bufOffset + wIndex + 1] = data[dataIndex
644: + wIndex + 1];
645: buf[bufOffset + wIndex] = data[dataIndex
646: + wIndex];
647: }
648: bufOffset += bmpBpl;
649: dataIndex -= imageBpl;
650: }
651: out.write(buf, 0, bufOffset);
652: }
653: } else {
654: for (int y = 0; y < image.height; y += linesPerBuf) {
655: int tmp = image.height - y;
656: int count = tmp < linesPerBuf ? tmp : linesPerBuf;
657: int bufOffset = 0;
658: for (int i = 0; i < count; i++) {
659: System.arraycopy(data, dataIndex, buf,
660: bufOffset, bpl);
661: bufOffset += bmpBpl;
662: dataIndex -= imageBpl;
663: }
664: out.write(buf, 0, bufOffset);
665: }
666: }
667: } catch (IOException e) {
668: SWT.error(SWT.ERROR_IO, e);
669: }
670: return bmpBpl * image.height;
671: }
672:
673: /**
674: * Unload a DeviceIndependentImage using Windows .BMP format into the given
675: * byte stream.
676: */
677: void unloadIntoByteStream(ImageLoader loader) {
678: ImageData image = loader.data[0];
679: byte[] rgbs;
680: int numCols;
681: if (!((image.depth == 1) || (image.depth == 4)
682: || (image.depth == 8) || (image.depth == 16)
683: || (image.depth == 24) || (image.depth == 32)))
684: SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH);
685: int comp = this .compression;
686: if (!((comp == 0) || ((comp == 1) && (image.depth == 8)) || ((comp == 2) && (image.depth == 4))))
687: SWT.error(SWT.ERROR_INVALID_IMAGE);
688: PaletteData pal = image.palette;
689: if ((image.depth == 16) || (image.depth == 24)
690: || (image.depth == 32)) {
691: if (!pal.isDirect)
692: SWT.error(SWT.ERROR_INVALID_IMAGE);
693: numCols = 0;
694: rgbs = null;
695: } else {
696: if (pal.isDirect)
697: SWT.error(SWT.ERROR_INVALID_IMAGE);
698: numCols = pal.colors.length;
699: rgbs = paletteToBytes(pal);
700: }
701: // Fill in file header, except for bfsize, which is done later.
702: int headersSize = BMPFileHeaderSize + BMPHeaderFixedSize;
703: int[] fileHeader = new int[5];
704: fileHeader[0] = 0x4D42; // Signature
705: fileHeader[1] = 0; // File size - filled in later
706: fileHeader[2] = 0; // Reserved 1
707: fileHeader[3] = 0; // Reserved 2
708: fileHeader[4] = headersSize; // Offset to data
709: if (rgbs != null) {
710: fileHeader[4] += rgbs.length;
711: }
712:
713: // Prepare data. This is done first so we don't have to try to rewind
714: // the stream and fill in the details later.
715: ByteArrayOutputStream out = new ByteArrayOutputStream();
716: unloadData(image, out, comp);
717: byte[] data = out.toByteArray();
718:
719: // Calculate file size
720: fileHeader[1] = fileHeader[4] + data.length;
721:
722: // Write the headers
723: try {
724: outputStream.writeShort(fileHeader[0]);
725: outputStream.writeInt(fileHeader[1]);
726: outputStream.writeShort(fileHeader[2]);
727: outputStream.writeShort(fileHeader[3]);
728: outputStream.writeInt(fileHeader[4]);
729: } catch (IOException e) {
730: SWT.error(SWT.ERROR_IO, e);
731: }
732: try {
733: outputStream.writeInt(BMPHeaderFixedSize);
734: outputStream.writeInt(image.width);
735: outputStream.writeInt(image.height);
736: outputStream.writeShort(1);
737: outputStream.writeShort((short) image.depth);
738: outputStream.writeInt(comp);
739: outputStream.writeInt(data.length);
740: outputStream.writeInt(pelsPerMeter.x);
741: outputStream.writeInt(pelsPerMeter.y);
742: outputStream.writeInt(numCols);
743: outputStream.writeInt(importantColors);
744: } catch (IOException e) {
745: SWT.error(SWT.ERROR_IO, e);
746: }
747:
748: // Unload palette
749: if (numCols > 0) {
750: try {
751: outputStream.write(rgbs);
752: } catch (IOException e) {
753: SWT.error(SWT.ERROR_IO, e);
754: }
755: }
756:
757: // Unload the data
758: try {
759: outputStream.write(data);
760: } catch (IOException e) {
761: SWT.error(SWT.ERROR_IO, e);
762: }
763: }
764:
765: void flipScanLines(byte[] data, int stride, int height) {
766: int i1 = 0;
767: int i2 = (height - 1) * stride;
768: for (int i = 0; i < height / 2; i++) {
769: for (int index = 0; index < stride; index++) {
770: byte b = data[index + i1];
771: data[index + i1] = data[index + i2];
772: data[index + i2] = b;
773: }
774: i1 += stride;
775: i2 -= stride;
776: }
777: }
778:
779: }
|