1
44 package org.xwt;
45
46 import org.xwt.util.*;
47 import java.io.BufferedInputStream;
48 import java.io.InputStream;
49 import java.io.IOException;
50 import java.io.PrintWriter;
51
52
53 public class GIF extends ImageDecoder {
54
55
56
57 public int[] getData() { return data; }
58 public int getWidth() { return width; }
59 public int getHeight() { return height; }
60
61
64 public static GIF decode(InputStream is, String name) {
65 try {
66 return new GIF(is, name);
67 } catch (Exception e) {
68 if (Log.on) Log.log(GIF.class, e);
69 return null;
70 }
71 }
72
73 private GIF(InputStream is, String name) throws IOException {
74 if (is instanceof BufferedInputStream) _in = (BufferedInputStream)is;
75 else _in = new BufferedInputStream(is);
76 decodeAsBufferedImage(0);
77 }
78
79
80
81 private int[] data = null;
82
83
84 private void decodeAsBufferedImage(int page) throws IOException, IOException {
85
86
87 if (page <= index ) return;
88
89
90 if (index < 0 ) {
91 if (!readIntoBuf(6) || _buf[0] != 'G' || _buf[1] != 'I' || _buf[2] != 'F' ||
92 _buf[3] != '8' || !(_buf[4] == '7' || _buf[4] == '9') || _buf[5] != 'a')
93 throw new IOException("Not a GIF8Xa file.");
94 if (!readGlobalImageDescriptor()) throw new IOException("Unable to read GIF header.");
95 }
96
97
98 int block_identifier;
99 while (true) {
100 if(!readIntoBuf(1)) throw new IOException("Unexpected EOF(1)");
101 block_identifier = _buf[0];
102 if (block_identifier == ';') throw new IOException("No image data");
103 if (block_identifier == '!') {
104 if (!readExtensionBlock()) throw new IOException("Unexpected EOF(2)");
105 continue;
106 }
107
108
109 if (block_identifier != ',') continue;
110
111 if (!readLocalImageDescriptor()) throw new IOException("Unexpected EOF(3)");
112 data = new int[width * height];
113 readImage();
114
115
116
117
118
119 index++;
120 if (index < page) continue;
121
122
123 break;
124 }
125
126
127 return;
128 }
129
130
131 private void readImage()
132 throws IOException, IOException {
133 int len = width;
134 int rows = height;
135 int initialCodeSize;
136 int v;
137 intintintint, ypos = 0, pass = 0, i;
138 int prefix[] = new int[(1 << MAX_LWZ_BITS)];
139 int append[] = new int[(1 << MAX_LWZ_BITS)];
140 int stack[] = new int[(1 << MAX_LWZ_BITS)*2];
141 int top_idx;
142 intintintintintintintint inCode, endCode, oldCode, maxCode, code, firstCode;
143
144
145 if (!readIntoBuf(1)) throw new IOException("Unexpected EOF decoding image");
146 initialCodeSize = _buf[0];
147
148
149 int[] cmap = global_color_map;
150 if (hascmap) cmap = color_map;
151 if (trans_idx >= 0) cmap[trans_idx] = 0x00000000;
152
153
154
160 clearCode = 1 << initialCodeSize;
161 endCode = clearCode + 1;
162 codeSize = initialCodeSize + 1;
163 maxCode = clearCode + 2;
164 oldCode = -1;
165 firstCode = -1;
166
167 for (i = 0; i < clearCode; i++) append[i] = i;
168 top_idx = 0;
169
170 bitsInWindow = 0;
171 bytes = 0;
172 window = 0L;
173 done = false;
174 c = -1;
175
176
177 ypos = 0;
178 for (i = 0; i < rows; i++) {
179 for (xpos = 0; xpos < len;) {
180
181 if (top_idx == 0) {
182
183 code = getCode(codeSize);
184 if (code < 0) return;
185
186 if (code > maxCode || code == endCode) return;
187 if (code == clearCode) {
188 codeSize = initialCodeSize + 1;
189 maxCode = clearCode + 2;
190 oldCode = -1;
191 continue;
192 }
193
194
195
196
197
198
199
200 if (oldCode == -1) {
201 stack[top_idx++] = append[code];
202 oldCode = code;
203 firstCode = code;
204 continue;
205 }
206
207 inCode = code;
208
209
210
211
212
213 if (code == maxCode) {
214 stack[top_idx++] = firstCode;
215 code = oldCode;
216 }
217
218
219
220 while (code > clearCode) {
221 stack[top_idx++] = append[code];
222 code = prefix[code];
223 }
224 firstCode = append[code];
225
226
227
228 if (maxCode >= (1 << MAX_LWZ_BITS)) return;
229
230
231 stack[top_idx++] = firstCode;
232
233
234 prefix[maxCode] = oldCode;
235 append[maxCode] = firstCode;
236 maxCode++;
237
238
239
240
241
242 if ((maxCode >= (1 << codeSize)) && (maxCode < (1<<MAX_LWZ_BITS))) codeSize++;
243 oldCode = inCode;
244 }
245
246
247 v = stack[--top_idx];
248 if (v < 0) return;
249
250
251 data[xpos + ypos * width] = cmap[v];
252 xpos++;
253 }
254
255
256 if (interlaced) {
257 ypos += _interlaceStep[pass];
258 while (ypos >= rows) {
259 pass++;
260 if (pass > 3) return;
261 ypos = _interlaceStart[pass];
262 }
263 } else ypos++;
264 }
265 return;
266 }
267
268
269 private int getCode(int code_size) throws IOException {
270 int ret;
271
272 while (bitsInWindow < code_size) {
273
274 if (done) return -1;
275
276 if (bytes == 0) {
277
278 bytes = getDataBlock();
279 c = 0;
280 if (bytes <= 0) {
281 done = true;
282 break;
283 }
284 }
285
286 window += (_buf[c]) << bitsInWindow;
287 ++c;
288 bitsInWindow += 8;
289 bytes--;
290 }
291
292
293
294 ret = ((int)window) & ((1 << code_size) - 1);
295
296
297 window >>= code_size;
298 bitsInWindow -= code_size;
299 return ret;
300 }
301
302
303 private boolean readGlobalImageDescriptor() throws IOException {
304 int packed;
305 int aspect;
306 int ofs;
307
308 if (!readIntoBuf(7) ) return false;
309 global_width = _buf[0] | (_buf[1] << 8);
310 global_height = _buf[2] | (_buf[3] << 8);
311 packed = _buf[4];
312 global_bgcolor = _buf[5];
313 aspect = _buf[6];
314 global_cmapsize = 2 << (packed & 0x07);
315 global_hascmap = (packed & GLOBALCOLORMAP) == GLOBALCOLORMAP;
316 global_color_map = null;
317
318
319 if (global_hascmap) {
320 if (!readColorMap(global_cmapsize,true)) {
321 return false;
322 }
323 }
324
325 return true;
326 }
327
328
329 private boolean readLocalImageDescriptor() throws IOException {
330 int packed;
331
332 if (!readIntoBuf(9) ) return false;
333
334 left = _buf[0] | (_buf[1] << 8);
335 top = _buf[2] | (_buf[3] << 8);
336 width = _buf[4] | (_buf[5] << 8);
337 height = _buf[6] | (_buf[7] << 8);
338 packed = _buf[8];
339 hascmap = (packed & LOCALCOLORMAP) == LOCALCOLORMAP;
340 cmapsize = 2 << (packed & 0x07);
341 interlaced = (packed & INTERLACE) == INTERLACE;
342 color_map = null;
343
344
345 return !(hascmap && !readColorMap(cmapsize,false));
346 }
347
348
349 private boolean readColorMap(int nColors, boolean isGlobal)
350 throws IOException {
351 int[] map = new int[nColors];
352 for( int i=0; i < nColors; ++i) {
353 if (!readIntoBuf(3) ) return false;
354 map[i] = (_buf[0] << 16) | (_buf[1] << 8) | _buf[2] | 0xFF000000;
355 }
356 if (isGlobal) global_color_map = map;
357 else color_map = map;
358 return true;
359 }
360
361
362 private boolean readExtensionBlock() throws IOException {
363 if (!readIntoBuf(1) ) return false;
364 int label = _buf[0];
365 int count = -1;
366 switch (label) {
367 case 0x01:
368 case 0xff:
369 case 0xfe:
370 break;
371 case 0xf9:
372 count = getDataBlock();
373 if (count < 0) return true;
374
375 if ((_buf[0] & HASTRANSPARENCY) != 0) trans_idx = _buf[3];
376 else trans_idx = -1;
377 }
378 do { count = getDataBlock(); } while (count > 0);
379 return true;
380 }
381
382
383 private int getDataBlock() throws IOException {
384 if (!readIntoBuf(1) ) return -1;
385 int count = _buf[0];
386 if (count != 0) if (!readIntoBuf(count) ) return -1;
387 return count;
388 }
389
390
391 private boolean readIntoBuf(int count) throws IOException {
392 for(int i = 0; i < count; i++) if ((_buf[i] = _in.read()) == -1) return false;
393 return true;
394 }
395
396
397
398
399 private int index = -1;
400 private BufferedInputStream _in = null;
401 private int[] _buf = new int[BUFSIZE];
402
403
404 private int trans_idx = -1;
405
406
407 private int global_width = 0;
408 private int global_height = 0;
409 private int global_bgcolor = 0;
410 private int global_cmapsize = 0;
411 private boolean global_hascmap = false;
412 private int[] global_color_map = null;
413
414
415 private int left = 0;
416 private int top = 0;
417 private int width = 0;
418 private int height = 0;
419 private int cmapsize = 0;
420 private boolean hascmap = false;
421 private boolean interlaced = false;
422 private int[] color_map = null;
423
424
425 private int bytes = 0;
426 private boolean done;
427 private int c;
428 private long window;
429 private int bitsInWindow = 0;
430
431
432 private static final int INTERLACE = 0x40;
433 private static final int GLOBALCOLORMAP = 0x80;
434 private static final int LOCALCOLORMAP = 0x80;
435 private static final int HASTRANSPARENCY = 0x01;
436 private static final int MAX_LWZ_BITS = 12;
437 private static final int BUFSIZE = 280;
438 private static final int[] _interlaceStep = { 8, 8, 4, 2 };
439 private static final int[] _interlaceStart = { 0, 4, 2, 1 };
440 }
441
442
443
444