Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
GifEncoder.java
1 package ij.io;
2 import java.util.*;
3 import java.io.*;
4 import java.awt.Image;
5 import java.awt.image.*;
6 
38 public class GifEncoder {
39 
40  private boolean interlace = false;
41  private int width, height;
42  private byte[] pixels;
43  private byte[] r, g, b; // the color look-up table
44  private int pixelIndex;
45  private int numPixels;
46  private int transparentPixel = -1; // hpm
47 
57  public GifEncoder(int width, int height, byte[] pixels, byte[] r, byte[] g, byte[] b) {
58  this.width = width;
59  this.height = height;
60  this.pixels = pixels;
61  this.r = r; this.g = g; this.b = b;
62  interlace = false;
63  pixelIndex = 0;
64  numPixels = width*height;
65  }
66 
69  public GifEncoder(Image img) {
70  width = img.getWidth(null);
71  height = img.getHeight(null);
72  pixels = new byte[width * height];
73  PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, false);
74  try {
75  pg.grabPixels();
76  } catch (InterruptedException e) {
77  System.err.println(e);
78  };
79  ColorModel cm = pg.getColorModel();
80  if (cm instanceof IndexColorModel)
81  {
82  pixels = (byte[])(pg.getPixels());
83  // hpm
84  IndexColorModel icm = (IndexColorModel)cm;
85  setTransparentPixel(icm.getTransparentPixel());
86  }
87  else
88  throw new IllegalArgumentException("Image must be 8-bit");
89  IndexColorModel m = (IndexColorModel)cm;
90  int mapSize = m.getMapSize();
91  r = new byte[mapSize];
92  g = new byte[mapSize];
93  b = new byte[mapSize];
94  m.getReds(r);
95  m.getGreens(g);
96  m.getBlues(b);
97  interlace = false;
98  pixelIndex = 0;
99  numPixels = width*height;
100 
101  }
102 
104  public void write(OutputStream out) throws IOException {
105  // Figure out how many bits to use.
106  int numColors = r.length;
107  int BitsPerPixel;
108  if (numColors<=2)
109  BitsPerPixel = 1;
110  else if (numColors<=4)
111  BitsPerPixel = 2;
112  else if (numColors<=16)
113  BitsPerPixel = 4;
114  else
115  BitsPerPixel = 8;
116 
117  int ColorMapSize = 1 << BitsPerPixel;
118  byte[] reds = new byte[ColorMapSize];
119  byte[] grns = new byte[ColorMapSize];
120  byte[] blus = new byte[ColorMapSize];
121  for (int i=0; i<numColors; i++) {
122  reds[i] = r[i];
123  grns[i] = g[i];
124  blus[i] = b[i];
125  }
126 
127  // hpm
128  GIFEncode(out, width, height, interlace, (byte) 0,
129  getTransparentPixel(), BitsPerPixel, reds, grns, blus);
130  }
131 
132  // hpm
133  public void setTransparentPixel(int pixel) {
134  transparentPixel = pixel;
135  }
136 
137  // hpm
138  public int getTransparentPixel() {
139  return transparentPixel;
140  }
141 
142  static void writeString(OutputStream out, String str) throws IOException
143  {
144  byte[] buf = str.getBytes();
145  out.write(buf);
146  }
147 
148  // Adapted from ppmtogif, which is based on GIFENCOD by David
149  // Rowley <mgardi@watdscu.waterloo.edu>. Lempel-Zim compression
150  // based on "compress".
151 
152  int Width, Height;
153  boolean Interlace;
154 
155  void GIFEncode(
156  OutputStream outs, int Width, int Height, boolean Interlace, byte Background, int Transparent, int BitsPerPixel, byte[] Red, byte[] Green, byte[] Blue )
157  throws IOException
158  {
159  byte B;
160  int LeftOfs, TopOfs;
161  int ColorMapSize;
162  int InitCodeSize;
163  int i;
164 
165  this.Width = Width;
166  this.Height = Height;
167  this.Interlace = Interlace;
168  ColorMapSize = 1 << BitsPerPixel;
169  LeftOfs = TopOfs = 0;
170 
171  // The initial code size
172  if ( BitsPerPixel <= 1 )
173  InitCodeSize = 2;
174  else
175  InitCodeSize = BitsPerPixel;
176 
177  // Write the Magic header
178  writeString( outs, "GIF89a" );
179 
180  // Write out the screen width and height
181  Putword( Width, outs );
182  Putword( Height, outs );
183 
184  // Indicate that there is a global colour map
185  B = (byte) 0x80; // Yes, there is a color map
186  // OR in the resolution
187  B |= (byte) ( ( 8 - 1 ) << 4 );
188  // Not sorted
189  // OR in the Bits per Pixel
190  B |= (byte) ( ( BitsPerPixel - 1 ) );
191 
192  // Write it out
193  Putbyte( B, outs );
194 
195  // Write out the Background colour
196  Putbyte( Background, outs );
197 
198  // Pixel aspect ratio - 1:1.
199  //Putbyte( (byte) 49, outs );
200  // Java's GIF reader currently has a bug, if the aspect ratio byte is
201  // not zero it throws an ImageFormatException. It doesn't know that
202  // 49 means a 1:1 aspect ratio. Well, whatever, zero works with all
203  // the other decoders I've tried so it probably doesn't hurt.
204  Putbyte( (byte) 0, outs );
205 
206  // Write out the Global Colour Map
207  for ( i = 0; i < ColorMapSize; ++i )
208  {
209  Putbyte( Red[i], outs );
210  Putbyte( Green[i], outs );
211  Putbyte( Blue[i], outs );
212  }
213 
214  // Write out extension for transparent colour index, if necessary.
215  if ( Transparent != -1 )
216  {
217  Putbyte( (byte) '!', outs );
218  Putbyte( (byte) 0xf9, outs );
219  Putbyte( (byte) 4, outs );
220  Putbyte( (byte) 1, outs );
221  Putbyte( (byte) 0, outs );
222  Putbyte( (byte) 0, outs );
223  Putbyte( (byte) Transparent, outs );
224  Putbyte( (byte) 0, outs );
225  }
226 
227  // Write an Image separator
228  Putbyte( (byte) ',', outs );
229 
230  // Write the Image header
231  Putword( LeftOfs, outs );
232  Putword( TopOfs, outs );
233  Putword( Width, outs );
234  Putword( Height, outs );
235 
236  // Write out whether or not the image is interlaced
237  if ( Interlace )
238  Putbyte( (byte) 0x40, outs );
239  else
240  Putbyte( (byte) 0x00, outs );
241 
242  // Write out the initial code size
243  Putbyte( (byte) InitCodeSize, outs );
244 
245  // Go and actually compress the data
246  compress( InitCodeSize+1, outs );
247 
248  // Write out a Zero-length packet (to end the series)
249  Putbyte( (byte) 0, outs );
250 
251  // Write the GIF file terminator
252  Putbyte( (byte) ';', outs );
253  }
254 
255 
256  static final int EOF = -1;
257 
258  // Return the next pixel from the image
259  int GIFNextPixel() throws IOException {
260  if (pixelIndex==numPixels)
261  return EOF;
262  else
263  return ((byte[])pixels)[pixelIndex++] & 0xff;
264  }
265 
266 
267  // Write out a word to the GIF file
268  void Putword( int w, OutputStream outs ) throws IOException
269  {
270  Putbyte( (byte) ( w & 0xff ), outs );
271  Putbyte( (byte) ( ( w >> 8 ) & 0xff ), outs );
272  }
273 
274  // Write out a byte to the GIF file
275  void Putbyte( byte b, OutputStream outs ) throws IOException
276  {
277  outs.write( b );
278  }
279 
280 
281  // GIFCOMPR.C - GIF Image compression routines
282  //
283  // Lempel-Ziv compression based on 'compress'. GIF modifications by
284  // David Rowley (mgardi@watdcsu.waterloo.edu)
285 
286  // General DEFINEs
287 
288  static final int BITS = 12;
289 
290  static final int HSIZE = 5003; // 80% occupancy
291 
292  // GIF Image compression - modified 'compress'
293  //
294  // Based on: compress.c - File compression ala IEEE Computer, June 1984.
295  //
296  // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
297  // Jim McKie (decvax!mcvax!jim)
298  // Steve Davies (decvax!vax135!petsd!peora!srd)
299  // Ken Turkowski (decvax!decwrl!turtlevax!ken)
300  // James A. Woods (decvax!ihnp4!ames!jaw)
301  // Joe Orost (decvax!vax135!petsd!joe)
302 
303  int n_bits; // number of bits/code
304  int maxbits = BITS; // user settable max # bits/code
305  int maxcode; // maximum code, given n_bits
306  int maxmaxcode = 1 << BITS; // should NEVER generate this code
307 
308  final int MAXCODE( int n_bits )
309  {
310  return ( 1 << n_bits ) - 1;
311  }
312 
313  int[] htab = new int[HSIZE];
314  int[] codetab = new int[HSIZE];
315 
316  int hsize = HSIZE; // for dynamic table sizing
317 
318  int free_ent = 0; // first unused entry
319 
320  // block compression parameters -- after all codes are used up,
321  // and compression rate changes, start over.
322  boolean clear_flg = false;
323 
324  // Algorithm: use open addressing double hashing (no chaining) on the
325  // prefix code / next character combination. We do a variant of Knuth's
326  // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
327  // secondary probe. Here, the modular division first probe is gives way
328  // to a faster exclusive-or manipulation. Also do block compression with
329  // an adaptive reset, whereby the code table is cleared when the compression
330  // ratio decreases, but after the table fills. The variable-length output
331  // codes are re-sized at this point, and a special CLEAR code is generated
332  // for the decompressor. Late addition: construct the table according to
333  // file size for noticeable speed improvement on small files. Please direct
334  // questions about this implementation to ames!jaw.
335 
336  int g_init_bits;
337 
338  int ClearCode;
339  int EOFCode;
340 
341  void compress( int init_bits, OutputStream outs ) throws IOException
342  {
343  int fcode;
344  int i /* = 0 */;
345  int c;
346  int ent;
347  int disp;
348  int hsize_reg;
349  int hshift;
350 
351  // Set up the globals: g_init_bits - initial number of bits
352  g_init_bits = init_bits;
353 
354  // Set up the necessary values
355  clear_flg = false;
356  n_bits = g_init_bits;
357  maxcode = MAXCODE( n_bits );
358 
359  ClearCode = 1 << ( init_bits - 1 );
360  EOFCode = ClearCode + 1;
361  free_ent = ClearCode + 2;
362 
363  char_init();
364 
365  ent = GIFNextPixel();
366 
367  hshift = 0;
368  for ( fcode = hsize; fcode < 65536; fcode *= 2 )
369  ++hshift;
370  hshift = 8 - hshift; // set hash code range bound
371 
372  hsize_reg = hsize;
373  cl_hash( hsize_reg ); // clear hash table
374 
375  output( ClearCode, outs );
376 
377  outer_loop:
378  while ( (c = GIFNextPixel()) != EOF )
379  {
380  fcode = ( c << maxbits ) + ent;
381  i = ( c << hshift ) ^ ent; // xor hashing
382 
383  if ( htab[i] == fcode )
384  {
385  ent = codetab[i];
386  continue;
387  }
388  else if ( htab[i] >= 0 ) // non-empty slot
389  {
390  disp = hsize_reg - i; // secondary hash (after G. Knott)
391  if ( i == 0 )
392  disp = 1;
393  do
394  {
395  if ( (i -= disp) < 0 )
396  i += hsize_reg;
397 
398  if ( htab[i] == fcode )
399  {
400  ent = codetab[i];
401  continue outer_loop;
402  }
403  }
404  while ( htab[i] >= 0 );
405  }
406  output( ent, outs );
407  ent = c;
408  if ( free_ent < maxmaxcode )
409  {
410  codetab[i] = free_ent++; // code -> hashtable
411  htab[i] = fcode;
412  }
413  else
414  cl_block( outs );
415  }
416  // Put out the final code.
417  output( ent, outs );
418  output( EOFCode, outs );
419  }
420 
421  // output
422  //
423  // Output the given code.
424  // Inputs:
425  // code: A n_bits-bit integer. If == -1, then EOF. This assumes
426  // that n_bits =< wordsize - 1.
427  // Outputs:
428  // Outputs code to the file.
429  // Assumptions:
430  // Chars are 8 bits long.
431  // Algorithm:
432  // Maintain a BITS character long buffer (so that 8 codes will
433  // fit in it exactly). Use the VAX insv instruction to insert each
434  // code in turn. When the buffer fills up empty it and start over.
435 
436  int cur_accum = 0;
437  int cur_bits = 0;
438 
439  int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
440  0x001F, 0x003F, 0x007F, 0x00FF,
441  0x01FF, 0x03FF, 0x07FF, 0x0FFF,
442  0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
443 
444  void output( int code, OutputStream outs ) throws IOException
445  {
446  cur_accum &= masks[cur_bits];
447 
448  if ( cur_bits > 0 )
449  cur_accum |= ( code << cur_bits );
450  else
451  cur_accum = code;
452 
453  cur_bits += n_bits;
454 
455  while ( cur_bits >= 8 )
456  {
457  char_out( (byte) ( cur_accum & 0xff ), outs );
458  cur_accum >>= 8;
459  cur_bits -= 8;
460  }
461 
462  // If the next entry is going to be too big for the code size,
463  // then increase it, if possible.
464  if ( free_ent > maxcode || clear_flg )
465  {
466  if ( clear_flg )
467  {
468  maxcode = MAXCODE(n_bits = g_init_bits);
469  clear_flg = false;
470  }
471  else
472  {
473  ++n_bits;
474  if ( n_bits == maxbits )
475  maxcode = maxmaxcode;
476  else
477  maxcode = MAXCODE(n_bits);
478  }
479  }
480 
481  if ( code == EOFCode )
482  {
483  // At EOF, write the rest of the buffer.
484  while ( cur_bits > 0 )
485  {
486  char_out( (byte) ( cur_accum & 0xff ), outs );
487  cur_accum >>= 8;
488  cur_bits -= 8;
489  }
490 
491  flush_char( outs );
492  }
493  }
494 
495  // Clear out the hash table
496 
497  // table clear for block compress
498  void cl_block( OutputStream outs ) throws IOException
499  {
500  cl_hash( hsize );
501  free_ent = ClearCode + 2;
502  clear_flg = true;
503 
504  output( ClearCode, outs );
505  }
506 
507  // reset code table
508  void cl_hash( int hsize )
509  {
510  for ( int i = 0; i < hsize; ++i )
511  htab[i] = -1;
512  }
513 
514  // GIF Specific routines
515 
516  // Number of characters so far in this 'packet'
517  int a_count;
518 
519  // Set up the 'byte output' routine
520  void char_init()
521  {
522  a_count = 0;
523  }
524 
525  // Define the storage for the packet accumulator
526  byte[] accum = new byte[256];
527 
528  // Add a character to the end of the current packet, and if it is 254
529  // characters, flush the packet to disk.
530  void char_out( byte c, OutputStream outs ) throws IOException
531  {
532  accum[a_count++] = c;
533  if ( a_count >= 254 )
534  flush_char( outs );
535  }
536 
537  // Flush the packet to disk, and reset the accumulator
538  void flush_char( OutputStream outs ) throws IOException
539  {
540  if ( a_count > 0 )
541  {
542  outs.write( a_count );
543  outs.write( accum, 0, a_count );
544  a_count = 0;
545  }
546  }
547 
548  }
549 
550 class GifEncoderHashitem {
551  public int rgb;
552  public int count;
553  public int index;
554  public boolean isTransparent;
555 
556  public GifEncoderHashitem(int rgb, int count, int index, boolean isTransparent) {
557  this.rgb = rgb;
558  this.count = count;
559  this.index = index;
560  this.isTransparent = isTransparent;
561  }
562 
563 }