/Users/lyon/j4p/src/ip/vs/WriteGIF.java
|
1 /**
2 * WriteGIF is a class that takes an Image and saves it to
3 * a GIF format file.
4 *
5 * Based upon gifsave.c, which was written and released by
6 * Sverre H. Huseby. Ported to Java by Adam Doppelt of Brown
7 * University.
8 * Modified and integrated with the ImageProc program for
9 * CS411X, advanced Java Programming at the University of
10 * Bridgeport.
11 * Color quantization will be added by
12 * Victor Silva (victor@cse.bridgeport.edu).
13 *
14 */
15 package ip.vs;
16
17 import java.awt.*;
18 import java.awt.image.PixelGrabber;
19 import java.io.BufferedOutputStream;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.OutputStream;
23
24 public class WriteGIF {
25 short width_, height_;
26 int numColors_;
27 byte pixels_[], colors_[];
28
29 ScreenDescriptor sd_;
30 ImageDescriptor id_;
31
32 public static void DoIt(Image img, String fname)
33 throws IOException, java.awt.AWTException {
34 // encode the image as a GIF
35 WriteGIF encode = new WriteGIF(img);
36
37 OutputStream output = new BufferedOutputStream(
38 new FileOutputStream(fname));
39 encode.Write(output);
40
41 }
42
43 public WriteGIF(Image image) throws AWTException {
44 width_ = (short) image.getWidth(null);
45 height_ = (short) image.getHeight(null);
46
47 int values[] = new int[width_ * height_];
48 PixelGrabber grabber = new PixelGrabber(
49 image, 0, 0, width_, height_, values, 0, width_);
50
51 try {
52 if (grabber.grabPixels() != true)
53 throw new AWTException("Grabber returned false: " +
54 grabber.status());
55 } catch (InterruptedException e) {
56 }
57 ;
58
59 //--------------------------------------------------------------
60 // Possible Speed up - do it a row at a time, a la ACME
61 //--------------------------------------------------------------
62 byte r[][] = new byte[width_][height_];
63 byte g[][] = new byte[width_][height_];
64 byte b[][] = new byte[width_][height_];
65 int index = 0;
66
67 for (int y = 0; y < height_; ++y) {
68 for (int x = 0; x < width_; ++x) {
69 r[x][y] = (byte) ((values[index] >> 16) & 0xFF);
70 g[x][y] = (byte) ((values[index] >> 8) & 0xFF);
71 b[x][y] = (byte) ((values[index]) & 0xFF);
72 ++index;
73 }
74 }
75 ToIndexedColor(r, g, b);
76 }
77
78
79 //--------------------------------------------------------------
80 // CONSTRUCTOR
81 //--------------------------------------------------------------
82 public WriteGIF(byte r[][], byte g[][], byte b[][])
83 throws AWTException {
84 width_ = (short) (r.length);
85 height_ = (short) (r[0].length);
86
87 ToIndexedColor(r, g, b);
88 }
89
90 public void Write(OutputStream output) throws IOException {
91 BitUtils.WriteString(output, "GIF87a");
92 ScreenDescriptor sd = new ScreenDescriptor(width_, height_,
93 numColors_);
94 sd.Write(output);
95 output.write(colors_, 0, colors_.length);
96 ImageDescriptor id = new ImageDescriptor(width_, height_, ',');
97 id.Write(output);
98
99 byte codesize = BitUtils.BitsNeeded(numColors_);
100 if (codesize == 1) {
101 ++codesize;
102 }
103 output.write(codesize);
104
105 LZWCompressor.LZWCompress(output, codesize, pixels_);
106 output.write(0);
107
108 id = new ImageDescriptor((byte) 0, (byte) 0, ';');
109 id.Write(output);
110 output.flush();
111 }
112
113 void ToIndexedColor(byte r[][], byte g[][], byte b[][])
114 throws AWTException {
115 pixels_ = new byte[width_ * height_];
116 colors_ = new byte[256 * 3];
117 int colornum = 0;
118 for (int x = 0; x < width_; ++x) {
119 for (int y = 0; y < height_; ++y) {
120 int search;
121 for (search = 0; search < colornum; ++search) {
122 if (colors_[search * 3] == r[x][y] &&
123 colors_[search * 3 + 1] == g[x][y] &&
124 colors_[search * 3 + 2] == b[x][y]) {
125 break;
126 }
127 }
128
129 // If there are more than 256 colors invoke
130 // the color quantization procedure.
131 //quantization();
132 if (search > 255) {
133 throw new AWTException("Too many colors.");
134 }
135
136 pixels_[y * width_ + x] = (byte) search;
137
138 if (search == colornum) {
139 colors_[search * 3] = r[x][y];
140 colors_[search * 3 + 1] = g[x][y];
141 colors_[search * 3 + 2] = b[x][y];
142 ++colornum;
143 }
144 }
145 }
146 numColors_ = 1 << BitUtils.BitsNeeded(colornum);
147 byte copy[] = new byte[numColors_ * 3];
148 System.arraycopy(colors_, 0, copy, 0, numColors_ * 3);
149 colors_ = copy;
150 }
151 }
152
153 class BitFile {
154 OutputStream output_;
155 byte buffer_[];
156 int index_, bitsLeft_;
157
158 public BitFile(OutputStream output) {
159 output_ = output;
160 buffer_ = new byte[256];
161 index_ = 0;
162 bitsLeft_ = 8;
163 }
164
165 public void Flush() throws IOException {
166 int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
167 if (numBytes > 0) {
168 output_.write(numBytes);
169 output_.write(buffer_, 0, numBytes);
170 buffer_[0] = 0;
171 index_ = 0;
172 bitsLeft_ = 8;
173 }
174 }
175
176 public void WriteBits(int bits, int numbits) throws IOException {
177 int bitsWritten = 0;
178 int numBytes = 255;
179 do {
180 if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254) {
181 output_.write(numBytes);
182 output_.write(buffer_, 0, numBytes);
183
184 buffer_[0] = 0;
185 index_ = 0;
186 bitsLeft_ = 8;
187 }
188
189 if (numbits <= bitsLeft_) {
190 buffer_[index_] |= (bits & ((1 << numbits) - 1)) <<
191 (8 - bitsLeft_);
192 bitsWritten += numbits;
193 bitsLeft_ -= numbits;
194 numbits = 0;
195 } else {
196 buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) <<
197 (8 - bitsLeft_);
198 bitsWritten += bitsLeft_;
199 bits >>= bitsLeft_;
200 numbits -= bitsLeft_;
201 buffer_[++index_] = 0;
202 bitsLeft_ = 8;
203 }
204 } while (numbits != 0);
205 }
206 }
207
208 class LZWStringTable {
209 private final static int RES_CODES = 2;
210 private final static short HASH_FREE = (short) 0xFFFF;
211 private final static short NEXT_FIRST = (short) 0xFFFF;
212 private final static int MAXBITS = 12;
213 private final static int MAXSTR = (1 << MAXBITS);
214 private final static short HASHSIZE = 9973;
215 private final static short HASHSTEP = 2039;
216
217 byte strChr_[];
218 short strNxt_[];
219 short strHsh_[];
220 short numStrings_;
221
222 public LZWStringTable() {
223 strChr_ = new byte[MAXSTR];
224 strNxt_ = new short[MAXSTR];
225 strHsh_ = new short[HASHSIZE];
226 }
227
228 public int AddCharString(short index, byte b) {
229 int hshidx;
230
231 if (numStrings_ >= MAXSTR) {
232 return 0xFFFF;
233 }
234
235 hshidx = Hash(index, b);
236 while (strHsh_[hshidx] != HASH_FREE) {
237 hshidx = (hshidx + HASHSTEP) % HASHSIZE;
238 }
239
240 strHsh_[hshidx] = numStrings_;
241 strChr_[numStrings_] = b;
242 strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
243
244 return numStrings_++;
245 }
246
247 public short FindCharString(short index, byte b) {
248 int hshidx, nxtidx;
249
250 if (index == HASH_FREE) {
251 return b;
252 }
253
254 hshidx = Hash(index, b);
255 while ((nxtidx = strHsh_[hshidx]) != HASH_FREE) {
256 if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b) {
257 return (short) nxtidx;
258 }
259 hshidx = (hshidx + HASHSTEP) % HASHSIZE;
260 }
261
262 return (short) 0xFFFF;
263 }
264
265 public void ClearTable(int codesize) {
266 numStrings_ = 0;
267
268 for (int q = 0; q < HASHSIZE; q++) {
269 strHsh_[q] = HASH_FREE;
270 }
271
272 int w = (1 << codesize) + RES_CODES;
273 for (int q = 0; q < w; q++) {
274 AddCharString((short) 0xFFFF, (byte) q);
275 }
276 }
277
278 static public int Hash(short index, byte lastbyte) {
279 return ((int) ((short) (lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
280 }
281 }
282
283 class LZWCompressor {
284 public static void LZWCompress(OutputStream output, int codesize,
285 byte toCompress[]) throws IOException {
286 byte c;
287 short index;
288 int clearcode, endofinfo, numbits, limit, errcode;
289 short prefix = (short) 0xFFFF;
290
291 BitFile bitFile = new BitFile(output);
292 LZWStringTable strings = new LZWStringTable();
293
294 clearcode = 1 << codesize;
295 endofinfo = clearcode + 1;
296
297 numbits = codesize + 1;
298 limit = (1 << numbits) - 1;
299
300 strings.ClearTable(codesize);
301 bitFile.WriteBits(clearcode, numbits);
302
303 for (int loop = 0; loop < toCompress.length; ++loop) {
304 c = toCompress[loop];
305 if ((index = strings.FindCharString(prefix, c)) != -1) {
306 prefix = index;
307 } else {
308 bitFile.WriteBits(prefix, numbits);
309 if (strings.AddCharString(prefix, c) > limit) {
310 if (++numbits > 12) {
311 bitFile.WriteBits(clearcode, numbits - 1);
312 strings.ClearTable(codesize);
313 numbits = codesize + 1;
314 }
315 limit = (1 << numbits) - 1;
316 }
317
318 prefix = (short) ((short) c & 0xFF);
319 }
320 }
321
322 if (prefix != -1) {
323 bitFile.WriteBits(prefix, numbits);
324 }
325
326 bitFile.WriteBits(endofinfo, numbits);
327 bitFile.Flush();
328 }
329 }
330
331 class ScreenDescriptor {
332 public short localScreenWidth_, localScreenHeight_;
333 private byte byte_;
334 public byte backgroundColorIndex_, pixelAspectRatio_;
335
336 public ScreenDescriptor(short width, short height, int numColors) {
337 localScreenWidth_ = width;
338 localScreenHeight_ = height;
339 SetGlobalColorTableSize((byte) (BitUtils.BitsNeeded(numColors) - 1));
340 SetGlobalColorTableFlag((byte) 1);
341 SetSortFlag((byte) 0);
342 SetColorResolution((byte) 7);
343 backgroundColorIndex_ = 0;
344 pixelAspectRatio_ = 0;
345 }
346
347 public void Write(OutputStream output) throws IOException {
348 BitUtils.WriteWord(output, localScreenWidth_);
349 BitUtils.WriteWord(output, localScreenHeight_);
350 output.write(byte_);
351 output.write(backgroundColorIndex_);
352 output.write(pixelAspectRatio_);
353 }
354
355 public void SetGlobalColorTableSize(byte num) {
356 byte_ |= (num & 7);
357 }
358
359 public void SetSortFlag(byte num) {
360 byte_ |= (num & 1) << 3;
361 }
362
363 public void SetColorResolution(byte num) {
364 byte_ |= (num & 7) << 4;
365 }
366
367 public void SetGlobalColorTableFlag(byte num) {
368 byte_ |= (num & 1) << 7;
369 }
370 }
371
372 class ImageDescriptor {
373 public byte separator_;
374 public short leftPosition_, topPosition_, width_, height_;
375 private byte byte_;
376
377 public ImageDescriptor(short width, short height, char separator) {
378 separator_ = (byte) separator;
379 leftPosition_ = 0;
380 topPosition_ = 0;
381 width_ = width;
382 height_ = height;
383 SetLocalColorTableSize((byte) 0);
384 SetReserved((byte) 0);
385 SetSortFlag((byte) 0);
386 SetInterlaceFlag((byte) 0);
387 SetLocalColorTableFlag((byte) 0);
388 }
389
390 public void Write(OutputStream output) throws IOException {
391 output.write(separator_);
392 BitUtils.WriteWord(output, leftPosition_);
393 BitUtils.WriteWord(output, topPosition_);
394 BitUtils.WriteWord(output, width_);
395 BitUtils.WriteWord(output, height_);
396 output.write(byte_);
397 }
398
399 public void SetLocalColorTableSize(byte num) {
400 byte_ |= (num & 7);
401 }
402
403 public void SetReserved(byte num) {
404 byte_ |= (num & 3) << 3;
405 }
406
407 public void SetSortFlag(byte num) {
408 byte_ |= (num & 1) << 5;
409 }
410
411 public void SetInterlaceFlag(byte num) {
412 byte_ |= (num & 1) << 6;
413 }
414
415 public void SetLocalColorTableFlag(byte num) {
416 byte_ |= (num & 1) << 7;
417 }
418 }
419
420 class BitUtils {
421 public static byte BitsNeeded(int n) {
422 byte ret = 1;
423
424 if (n-- == 0) {
425 return 0;
426 }
427
428 while ((n >>= 1) != 0) {
429 ++ret;
430 }
431
432 return ret;
433 }
434
435 public static void WriteWord(OutputStream output, short w)
436 throws IOException {
437 output.write(w & 0xFF);
438 output.write((w >> 8) & 0xFF);
439 }
440
441 static void WriteString(OutputStream output,
442 String string) throws IOException {
443 for (int loop = 0; loop < string.length(); ++loop) {
444 output.write((byte) (string.charAt(loop)));
445 }
446 }
447 }
448