Source code: com/port80/eclipse/util/XPMFile.java
1 package com.port80.eclipse.util;
2
3 import java.io.BufferedReader;
4 import java.io.FileInputStream;
5 import java.io.IOException;
6 import java.io.InputStreamReader;
7 import java.net.URL;
8 import java.util.StringTokenizer;
9
10 import com.port80.util.Msg;
11 import com.port80.util.struct.IntValueHashMap;
12 import com.port80.util.text.TextUtil;
13
14 /**
15 * Straightforward XPM file parser, which is unusually strict, but
16 * serves its purpose for most XPM files.
17 *
18 * HISTORY:
19 * . Switch back to use transparentPixel.
20 * alphaData is 10 times slower than maskData in SWT GC.drawImage() !
21 */
22 public class XPMFile {
23
24 ////////////////////////////////////////////////////////////////////////
25
26 private static final String NAME = "XPMFile";
27 private static final boolean DEBUG = false;
28
29 ////////////////////////////////////////////////////////////////////////
30
31 private String fFilename;
32 private BufferedReader fReader;
33 private int fRows, fColumns, fColors, fColorChars;
34
35 private IntValueHashMap fColorTable;
36 private String fTransparentSymbol;
37 private int fTransparent;
38 private int[] fData;
39 // private byte[] fAlphas;
40
41 ////////////////////////////////////////////////////////////////////////
42
43 public XPMFile(String filename) {
44 fColorTable = new IntValueHashMap(17);
45 fFilename = filename;
46 loadFile(filename);
47 }
48
49 public XPMFile(URL url) {
50 fColorTable = new IntValueHashMap(17);
51 fFilename = url.toString();
52 loadFile(url);
53 }
54
55 ////////////////////////////////////////////////////////////////////////
56
57 public String getFilename() {
58 return fFilename;
59 }
60
61 public int[] getData() {
62 return fData;
63 }
64
65 public int getRowCount() {
66 return fRows;
67 }
68
69 public int getColumnCount() {
70 return fColumns;
71 }
72
73 public int getTransparentColor() {
74 return fTransparent;
75 }
76
77 ////////////////////////////////////////////////////////////////////////
78
79 private void openFile(String filename) {
80 try {
81 fFilename = filename;
82 fReader = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
83 } catch (IOException ex) {
84 Msg.err("filename=" + filename, ex);
85 }
86 }
87
88 private boolean loadFile(URL url) {
89 try {
90 fColorTable.clear();
91 fReader = new BufferedReader(new InputStreamReader(url.openStream()));
92 parseFile();
93 } catch (IOException ioe) {
94 return false;
95 }
96 return true;
97 }
98
99 private boolean loadFile(String filename) {
100 try {
101 fColorTable.clear();
102 openFile(filename);
103 parseFile();
104 return true;
105 } catch (IOException ioe) {
106 return false;
107 }
108 }
109
110 private void parseFile() throws IOException {
111 String line;
112 do {
113 line = fReader.readLine();
114 } while (line != null && (line.length() == 0 || line.charAt(0) != '"'));
115 readMetaData(line.substring(1, line.length() - 2));
116 //
117 for (int i = 0; i < fColors; i++) {
118 readColor();
119 }
120 //
121 fData = new int[fRows * fColumns];
122 for (int i = 0; i < fRows; i++) {
123 readImageRow(i);
124 }
125 fTransparent = -1;
126 if (fTransparentSymbol != null) {
127 // FIXME:
128 // . As of Eclipse 2.1.0 GTK, eclipse (Label and Button) do not handle transparent
129 // icons correctly unless the transparentPixel==0. This hack move any color==0
130 // to 1 and always return fTransparent==0.
131 fTransparent = 0;
132 // // Look for first unused color. In most case, all these would do is just set fTransparent=0.
133 // int[] values = fColorTable.values();
134 // Arrays.sort(values);
135 // if (DEBUG)
136 // System.err.println(Sprint.array("values=", "%08x ", values));
137 // int last = -1;
138 // for (int i = 0; i < values.length; ++i) {
139 // // Unless color 0x000000 is used, otherwise fTransparent should be 0
140 // // since the first color is always -1.
141 // if (values[i] - last > 1) {
142 // fTransparent = last + 1;
143 // break;
144 // }
145 // last = values[i];
146 // }
147 // if (fTransparent < 0) {
148 // Msg.err(
149 // NAME
150 // + ".parseFile(): Can't find unused color: transparent symbol="
151 // + fTransparentSymbol);
152 // fTransparent = 0;
153 // }
154 // // Init. transparent mask data.
155 // for (int i = 0; i < fData.length; ++i) {
156 // if (fData[i] < 0) {
157 // fData[i] = fTransparent;
158 // }
159 // }
160 }
161 }
162
163 private void readMetaData(String line) throws IOException {
164 if (line == null) {
165 throw new IllegalStateException("File " + fFilename + " contains no data");
166 }
167 StringTokenizer t = new StringTokenizer(line, " \t");
168 try {
169 fColumns = Integer.parseInt(t.nextToken());
170 fRows = Integer.parseInt(t.nextToken());
171 fColors = Integer.parseInt(t.nextToken());
172 fColorChars = Integer.parseInt(t.nextToken());
173 } catch (Exception e) {
174 throw new IllegalStateException("Malformed meta data line: " + line);
175 }
176 }
177
178 /**
179 * Read a color row and remember it in the color table.
180 */
181 private void readColor() throws IOException {
182 String line;
183 do {
184 line = fReader.readLine();
185 } while (line != null && (line.length() == 0 || line.charAt(0) != '"'));
186 // Sanity check, min. length "s c #x"
187 if (line == null || line.length() < 8) {
188 throw new IllegalStateException("File " + fFilename + " ended prematurely");
189 }
190 int start = 1 + fColorChars;
191 int end = line.length() - 2;
192 String symbol = line.substring(1, start);
193 String hexval = null;
194 String s;
195 char c;
196 int index;
197 do {
198 start = TextUtil.skipSpaces(line, start, end);
199 while(start<end && !Character.isWhitespace(line.charAt(start))) ++start;
200 start = TextUtil.skipSpaces(line, start, end);
201 for (index = start; index < end; ++index) {
202 c = line.charAt(index);
203 if (c == ' ' || c == '\t' || c == 0xa0)
204 break;
205 }
206 s= line.substring(start, index);
207 c=line.charAt(start);
208 if(c=='#' || s.equalsIgnoreCase("none")) {
209 hexval = s;
210 break;
211 } else if(c=='"')
212 break;
213
214 start = index;
215 } while (start < end);
216 if (hexval == null) {
217 throw new IllegalStateException("File " + fFilename + " invalid color value: " + line);
218 }
219 // Deal with the transparent color
220 // FIXME:
221 // . As of Eclipse 2.1.0 GTK, eclipse (Label and Button) do not handle transparent
222 // icons correctly unless the transparentPixel==0. This hack move any color==0
223 // to 1 and always return fTransparent==0.
224 if (hexval.equalsIgnoreCase("none")) {
225 fTransparentSymbol = symbol;
226 // fColorTable.put(symbol, -1);
227 fColorTable.put(symbol, 0);
228 } else {
229 int color = convertColorString(hexval);
230 if (color == 0)
231 color = 1;
232 fColorTable.put(symbol, color);
233 }
234 }
235
236 private void readImageRow(int row) throws IOException {
237 String line;
238 do {
239 line = fReader.readLine();
240 } while (line != null && (line.length() == 0 || line.charAt(0) != '"'));
241 if (line == null) {
242 throw new IllegalStateException("Premature end of file " + fFilename);
243 }
244 line = line.substring(1, 1 + fColumns * fColorChars);
245 int offset = row * fColumns;
246 for (int i = 0; i < fColumns; i++) {
247 int start = i * fColorChars;
248 String c = line.substring(start, start + fColorChars);
249 fData[offset + i] = fColorTable.get(c);
250 }
251 }
252
253 private int convertColorString(String colString) {
254 return Integer.parseInt(colString.substring(1), 16);
255 }
256
257 ////////////////////////////////////////////////////////////////////////
258
259 public static void main(String[] args) throws IOException {
260 System.err.println("dir=" + System.getProperty("user.dir"));
261 XPMFile xpm = new XPMFile(args[0]);
262 System.err.println("Transparent=" + xpm.getTransparentColor());
263 ImageFactory.getDefault().create(xpm, "test");
264 }
265
266 ////////////////////////////////////////////////////////////////////////
267 }