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 WinICOFileFormat extends FileFormat {
017:
018: byte[] bitInvertData(byte[] data, int startIndex, int endIndex) {
019: // Destructively bit invert data in the given byte array.
020: for (int i = startIndex; i < endIndex; i++) {
021: data[i] = (byte) (255 - data[i - startIndex]);
022: }
023: return data;
024: }
025:
026: static final byte[] convertPad(byte[] data, int width, int height,
027: int depth, int pad, int newPad) {
028: if (pad == newPad)
029: return data;
030: int stride = (width * depth + 7) / 8;
031: int bpl = (stride + (pad - 1)) / pad * pad;
032: int newBpl = (stride + (newPad - 1)) / newPad * newPad;
033: byte[] newData = new byte[height * newBpl];
034: int srcIndex = 0, destIndex = 0;
035: for (int y = 0; y < height; y++) {
036: System
037: .arraycopy(data, srcIndex, newData, destIndex,
038: newBpl);
039: srcIndex += bpl;
040: destIndex += newBpl;
041: }
042: return newData;
043: }
044:
045: /**
046: * Answer the size in bytes of the file representation of the given
047: * icon
048: */
049: int iconSize(ImageData i) {
050: int shapeDataStride = (i.width * i.depth + 31) / 32 * 4;
051: int maskDataStride = (i.width + 31) / 32 * 4;
052: int dataSize = (shapeDataStride + maskDataStride) * i.height;
053: int paletteSize = i.palette.colors != null ? i.palette.colors.length * 4
054: : 0;
055: return WinBMPFileFormat.BMPHeaderFixedSize + paletteSize
056: + dataSize;
057: }
058:
059: boolean isFileFormat(LEDataInputStream stream) {
060: try {
061: byte[] header = new byte[4];
062: stream.read(header);
063: stream.unread(header);
064: return header[0] == 0 && header[1] == 0 && header[2] == 1
065: && header[3] == 0;
066: } catch (Exception e) {
067: return false;
068: }
069: }
070:
071: boolean isValidIcon(ImageData i) {
072: switch (i.depth) {
073: case 1:
074: case 4:
075: case 8:
076: if (i.palette.isDirect)
077: return false;
078: int size = i.palette.colors.length;
079: return size == 2 || size == 16 || size == 32 || size == 256;
080: case 24:
081: case 32:
082: return i.palette.isDirect;
083: }
084: return false;
085: }
086:
087: int loadFileHeader(LEDataInputStream byteStream) {
088: int[] fileHeader = new int[3];
089: try {
090: fileHeader[0] = byteStream.readShort();
091: fileHeader[1] = byteStream.readShort();
092: fileHeader[2] = byteStream.readShort();
093: } catch (IOException e) {
094: SWT.error(SWT.ERROR_IO, e);
095: }
096: if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
097: SWT.error(SWT.ERROR_INVALID_IMAGE);
098: int numIcons = fileHeader[2];
099: if (numIcons <= 0)
100: SWT.error(SWT.ERROR_INVALID_IMAGE);
101: return numIcons;
102: }
103:
104: int loadFileHeader(LEDataInputStream byteStream, boolean hasHeader) {
105: int[] fileHeader = new int[3];
106: try {
107: if (hasHeader) {
108: fileHeader[0] = byteStream.readShort();
109: fileHeader[1] = byteStream.readShort();
110: } else {
111: fileHeader[0] = 0;
112: fileHeader[1] = 1;
113: }
114: fileHeader[2] = byteStream.readShort();
115: } catch (IOException e) {
116: SWT.error(SWT.ERROR_IO, e);
117: }
118: if ((fileHeader[0] != 0) || (fileHeader[1] != 1))
119: SWT.error(SWT.ERROR_INVALID_IMAGE);
120: int numIcons = fileHeader[2];
121: if (numIcons <= 0)
122: SWT.error(SWT.ERROR_INVALID_IMAGE);
123: return numIcons;
124: }
125:
126: ImageData[] loadFromByteStream() {
127: int numIcons = loadFileHeader(inputStream);
128: int[][] headers = loadIconHeaders(numIcons);
129: ImageData[] icons = new ImageData[headers.length];
130: for (int i = 0; i < icons.length; i++) {
131: icons[i] = loadIcon(headers[i]);
132: }
133: return icons;
134: }
135:
136: /**
137: * Load one icon from the byte stream.
138: */
139: ImageData loadIcon(int[] iconHeader) {
140: byte[] infoHeader = loadInfoHeader(iconHeader);
141: WinBMPFileFormat bmpFormat = new WinBMPFileFormat();
142: bmpFormat.inputStream = inputStream;
143: PaletteData palette = bmpFormat.loadPalette(infoHeader);
144: byte[] shapeData = bmpFormat.loadData(infoHeader);
145: int width = (infoHeader[4] & 0xFF)
146: | ((infoHeader[5] & 0xFF) << 8)
147: | ((infoHeader[6] & 0xFF) << 16)
148: | ((infoHeader[7] & 0xFF) << 24);
149: int height = (infoHeader[8] & 0xFF)
150: | ((infoHeader[9] & 0xFF) << 8)
151: | ((infoHeader[10] & 0xFF) << 16)
152: | ((infoHeader[11] & 0xFF) << 24);
153: if (height < 0)
154: height = -height;
155: int depth = (infoHeader[14] & 0xFF)
156: | ((infoHeader[15] & 0xFF) << 8);
157: infoHeader[14] = 1;
158: infoHeader[15] = 0;
159: byte[] maskData = bmpFormat.loadData(infoHeader);
160: maskData = convertPad(maskData, width, height, 1, 4, 2);
161: bitInvertData(maskData, 0, maskData.length);
162: return ImageData.internal_new(width, height, depth, palette, 4,
163: shapeData, 2, maskData, null, -1, -1, SWT.IMAGE_ICO, 0,
164: 0, 0, 0);
165: }
166:
167: int[][] loadIconHeaders(int numIcons) {
168: int[][] headers = new int[numIcons][7];
169: try {
170: for (int i = 0; i < numIcons; i++) {
171: headers[i][0] = inputStream.read();
172: headers[i][1] = inputStream.read();
173: headers[i][2] = inputStream.readShort();
174: headers[i][3] = inputStream.readShort();
175: headers[i][4] = inputStream.readShort();
176: headers[i][5] = inputStream.readInt();
177: headers[i][6] = inputStream.readInt();
178: }
179: } catch (IOException e) {
180: SWT.error(SWT.ERROR_IO, e);
181: }
182: return headers;
183: }
184:
185: byte[] loadInfoHeader(int[] iconHeader) {
186: int width = iconHeader[0];
187: int height = iconHeader[1];
188: int numColors = iconHeader[2]; // the number of colors is in the low byte, but the high byte must be 0
189: if (numColors == 0)
190: numColors = 256; // this is specified: '00' represents '256' (0x100) colors
191: if ((numColors != 2) && (numColors != 8) && (numColors != 16)
192: && (numColors != 32) && (numColors != 256))
193: SWT.error(SWT.ERROR_INVALID_IMAGE);
194: if (inputStream.getPosition() < iconHeader[6]) {
195: // Seek to the specified offset
196: try {
197: inputStream.skip(iconHeader[6]
198: - inputStream.getPosition());
199: } catch (IOException e) {
200: SWT.error(SWT.ERROR_IO, e);
201: return null;
202: }
203: }
204: byte[] infoHeader = new byte[WinBMPFileFormat.BMPHeaderFixedSize];
205: try {
206: inputStream.read(infoHeader);
207: } catch (IOException e) {
208: SWT.error(SWT.ERROR_IO, e);
209: }
210: if (((infoHeader[12] & 0xFF) | ((infoHeader[13] & 0xFF) << 8)) != 1)
211: SWT.error(SWT.ERROR_INVALID_IMAGE);
212: int infoWidth = (infoHeader[4] & 0xFF)
213: | ((infoHeader[5] & 0xFF) << 8)
214: | ((infoHeader[6] & 0xFF) << 16)
215: | ((infoHeader[7] & 0xFF) << 24);
216: int infoHeight = (infoHeader[8] & 0xFF)
217: | ((infoHeader[9] & 0xFF) << 8)
218: | ((infoHeader[10] & 0xFF) << 16)
219: | ((infoHeader[11] & 0xFF) << 24);
220: int bitCount = (infoHeader[14] & 0xFF)
221: | ((infoHeader[15] & 0xFF) << 8);
222: if (height == infoHeight && bitCount == 1)
223: height /= 2;
224: if (!((width == infoWidth) && (height * 2 == infoHeight) && (bitCount == 1
225: || bitCount == 4 || bitCount == 8 || bitCount == 24 || bitCount == 32)))
226: SWT.error(SWT.ERROR_INVALID_IMAGE);
227: infoHeader[8] = (byte) (height & 0xFF);
228: infoHeader[9] = (byte) ((height >> 8) & 0xFF);
229: infoHeader[10] = (byte) ((height >> 16) & 0xFF);
230: infoHeader[11] = (byte) ((height >> 24) & 0xFF);
231: return infoHeader;
232: }
233:
234: /**
235: * Unload a single icon
236: */
237: void unloadIcon(ImageData icon) {
238: int sizeImage = (((icon.width * icon.depth + 31) / 32 * 4) + ((icon.width + 31) / 32 * 4))
239: * icon.height;
240: try {
241: outputStream.writeInt(WinBMPFileFormat.BMPHeaderFixedSize);
242: outputStream.writeInt(icon.width);
243: outputStream.writeInt(icon.height * 2);
244: outputStream.writeShort(1);
245: outputStream.writeShort((short) icon.depth);
246: outputStream.writeInt(0);
247: outputStream.writeInt(sizeImage);
248: outputStream.writeInt(0);
249: outputStream.writeInt(0);
250: outputStream
251: .writeInt(icon.palette.colors != null ? icon.palette.colors.length
252: : 0);
253: outputStream.writeInt(0);
254: } catch (IOException e) {
255: SWT.error(SWT.ERROR_IO, e);
256: }
257:
258: byte[] rgbs = WinBMPFileFormat.paletteToBytes(icon.palette);
259: try {
260: outputStream.write(rgbs);
261: } catch (IOException e) {
262: SWT.error(SWT.ERROR_IO, e);
263: }
264: unloadShapeData(icon);
265: unloadMaskData(icon);
266: }
267:
268: /**
269: * Unload the icon header for the given icon, calculating the offset.
270: */
271: void unloadIconHeader(ImageData i) {
272: int headerSize = 16;
273: int offset = headerSize + 6;
274: int iconSize = iconSize(i);
275: try {
276: outputStream.write(i.width);
277: outputStream.write(i.height);
278: outputStream
279: .writeShort(i.palette.colors != null ? i.palette.colors.length
280: : 0);
281: outputStream.writeShort(0);
282: outputStream.writeShort(0);
283: outputStream.writeInt(iconSize);
284: outputStream.writeInt(offset);
285: } catch (IOException e) {
286: SWT.error(SWT.ERROR_IO, e);
287: }
288: }
289:
290: void unloadIntoByteStream(ImageLoader loader) {
291: /* We do not currently support writing multi-image ico,
292: * so we use the first image data in the loader's array. */
293: ImageData image = loader.data[0];
294: if (!isValidIcon(image))
295: SWT.error(SWT.ERROR_INVALID_IMAGE);
296: try {
297: outputStream.writeShort(0);
298: outputStream.writeShort(1);
299: outputStream.writeShort(1);
300: } catch (IOException e) {
301: SWT.error(SWT.ERROR_IO, e);
302: }
303: unloadIconHeader(image);
304: unloadIcon(image);
305: }
306:
307: /**
308: * Unload the mask data for an icon. The data is flipped vertically
309: * and inverted.
310: */
311: void unloadMaskData(ImageData icon) {
312: ImageData mask = icon.getTransparencyMask();
313: int bpl = (icon.width + 7) / 8;
314: int pad = mask.scanlinePad;
315: int srcBpl = (bpl + pad - 1) / pad * pad;
316: int destBpl = (bpl + 3) / 4 * 4;
317: byte[] buf = new byte[destBpl];
318: int offset = (icon.height - 1) * srcBpl;
319: byte[] data = mask.data;
320: try {
321: for (int i = 0; i < icon.height; i++) {
322: System.arraycopy(data, offset, buf, 0, bpl);
323: bitInvertData(buf, 0, bpl);
324: outputStream.write(buf, 0, destBpl);
325: offset -= srcBpl;
326: }
327: } catch (IOException e) {
328: SWT.error(SWT.ERROR_IO, e);
329: }
330: }
331:
332: /**
333: * Unload the shape data for an icon. The data is flipped vertically.
334: */
335: void unloadShapeData(ImageData icon) {
336: int bpl = (icon.width * icon.depth + 7) / 8;
337: int pad = icon.scanlinePad;
338: int srcBpl = (bpl + pad - 1) / pad * pad;
339: int destBpl = (bpl + 3) / 4 * 4;
340: byte[] buf = new byte[destBpl];
341: int offset = (icon.height - 1) * srcBpl;
342: byte[] data = icon.data;
343: try {
344: for (int i = 0; i < icon.height; i++) {
345: System.arraycopy(data, offset, buf, 0, bpl);
346: outputStream.write(buf, 0, destBpl);
347: offset -= srcBpl;
348: }
349: } catch (IOException e) {
350: SWT.error(SWT.ERROR_IO, e);
351: }
352: }
353: }
|