/Users/lyon/j4p/src/ip/gif/gifAnimation/Gif89Frame.java

1    //****************************************************************************** 
2    // Gif89Frame.java 
3    //****************************************************************************** 
4    package ip.gif.gifAnimation; 
5     
6    import java.awt.*; 
7    import java.io.IOException; 
8    import java.io.OutputStream; 
9     
10   //============================================================================== 
11    
12   /** First off, just to dispel any doubt, this class and its subclasses have 
13    *  nothing to do with GUI "frames" such as java.awt.Frame.  We merely use the 
14    *  term in its very common sense of a still picture in an animation sequence. 
15    *  It's hoped that the restricted context will prevent any confusion. 
16    *  <p> 
17    *  An instance of this class is used in conjunction with a Gif89Encoder object 
18    *  to represent and encode a single static image and its associated "control" 
19    *  data.  A Gif89Frame doesn't know or care whether it is encoding one of the 
20    *  many animation frames in a GIF movie, or the single bitmap in a "normal" 
21    *  GIF. (FYI, this design mirrors the encoded GIF structure.) 
22    *  <p> 
23    *  Since Gif89Frame is an abstract class we don't instantiate it directly, but 
24    *  instead create instances of its concrete subclasses, IndexGif89Frame and 
25    *  DirectGif89Frame.  From the API standpoint, these subclasses differ only 
26    *  in the sort of data their instances are constructed from.  Most folks will 
27    *  probably work with DirectGif89Frame, since it can be constructed from a 
28    *  java.awt.Image object, but the lower-level IndexGif89Frame class offers 
29    *  advantages in specialized circumstances.  (Of course, in routine situations 
30    *  you might not explicitly instantiate any frames at all, instead letting 
31    *  Gif89Encoder's convenience methods do the honors.) 
32    *  <p> 
33    *  As far as the public API is concerned, objects in the Gif89Frame hierarchy 
34    *  interact with a Gif89Encoder only via the latter's methods for adding and 
35    *  querying frames.  (As a side note, you should know that while Gif89Encoder 
36    *  objects are permanently modified by the addition of Gif89Frames, the reverse 
37    *  is NOT true.  That is, even though the ultimate encoding of a Gif89Frame may 
38    *  be affected by the context its parent encoder object provides, it retains 
39    *  its original condition and can be reused in a different context.) 
40    *  <p> 
41    *  The core pixel-encoding code in this class was essentially lifted from 
42    *  Jef Poskanzer's well-known <cite>Acme GifEncoder</cite>, so please see the 
43    *  <a href="../readme.txt">readme</a> containing his notice. 
44    * 
45    * @version 0.90 beta (15-Jul-2000) 
46    * @author J. M. G. Elliott (tep@jmge.net) 
47    * @see Gif89Encoder 
48    * @see DirectGif89Frame 
49    * @see IndexGif89Frame 
50    */ 
51   public abstract class Gif89Frame { 
52    
53       //// Public "Disposal Mode" constants //// 
54    
55       /** The animated GIF renderer shall decide how to dispose of this Gif89Frame's 
56        *  display area. 
57        * @see Gif89Frame#setDisposalMode 
58        */ 
59       public static final int DM_UNDEFINED = 0; 
60    
61       /** The animated GIF renderer shall take no display-disposal action. 
62        * @see Gif89Frame#setDisposalMode 
63        */ 
64       public static final int DM_LEAVE = 1; 
65    
66       /** The animated GIF renderer shall replace this Gif89Frame's area with the 
67        *  background color. 
68        * @see Gif89Frame#setDisposalMode 
69        */ 
70       public static final int DM_BGCOLOR = 2; 
71    
72       /** The animated GIF renderer shall replace this Gif89Frame's area with the 
73        *  previous frame's bitmap. 
74        * @see Gif89Frame#setDisposalMode 
75        */ 
76       public static final int DM_REVERT = 3; 
77    
78       //// Bitmap variables set in package subclass constructors //// 
79       int theWidth = -1; 
80       int theHeight = -1; 
81       byte[] ciPixels; 
82    
83       //// GIF graphic frame control options //// 
84       private Point thePosition = new Point(0, 0); 
85       private boolean isInterlaced; 
86       private int csecsDelay; 
87       private int disposalCode = DM_LEAVE; 
88    
89       //---------------------------------------------------------------------------- 
90       /** Set the position of this frame within a larger animation display space. 
91        * 
92        * @param p 
93        *   Coordinates of the frame's upper left corner in the display space. 
94        *   (Default: The logical display's origin [0, 0]) 
95        * @see Gif89Encoder#setLogicalDisplay 
96        */ 
97       public void setPosition(Point p) { 
98           thePosition = new Point(p); 
99       } 
100   
101      //---------------------------------------------------------------------------- 
102      /** Set or clear the interlace flag. 
103       * 
104       * @param b 
105       *   true if you want interlacing.  (Default: false) 
106       */ 
107      public void setInterlaced(boolean b) { 
108          isInterlaced = b; 
109      } 
110   
111      //---------------------------------------------------------------------------- 
112      /** Set the between-frame interval. 
113       * 
114       * @param interval 
115       *   Centiseconds to wait before displaying the subsequent frame. 
116       *   (Default: 0) 
117       */ 
118      public void setDelay(int interval) { 
119          csecsDelay = interval; 
120      } 
121   
122      //---------------------------------------------------------------------------- 
123      /** Setting this option determines (in a cooperative GIF-viewer) what will be 
124       *  done with this frame's display area before the subsequent frame is 
125       *  displayed.  For instance, a setting of DM_BGCOLOR can be used for erasure 
126       *  when redrawing with displacement. 
127       * 
128       * @param code 
129       *   One of the four int constants of the Gif89Frame.DM_* series. 
130       *  (Default: DM_LEAVE) 
131       */ 
132      public void setDisposalMode(int code) { 
133          disposalCode = code; 
134      } 
135   
136      //---------------------------------------------------------------------------- 
137      Gif89Frame() { 
138      }  // package-visible default constructor 
139   
140      //---------------------------------------------------------------------------- 
141      abstract Object getPixelSource(); 
142   
143      //---------------------------------------------------------------------------- 
144      int getWidth() { 
145          return theWidth; 
146      } 
147   
148      //---------------------------------------------------------------------------- 
149      int getHeight() { 
150          return theHeight; 
151      } 
152   
153      //---------------------------------------------------------------------------- 
154      byte[] getPixelSink() { 
155          return ciPixels; 
156      } 
157   
158      //---------------------------------------------------------------------------- 
159      void encode(OutputStream os, boolean epluribus, int color_depth, 
160                  int transparent_index) throws IOException { 
161          writeGraphicControlExtension(os, epluribus, transparent_index); 
162          writeImageDescriptor(os); 
163          new GifPixelsEncoder( 
164                  theWidth, theHeight, ciPixels, isInterlaced, color_depth 
165          ).encode(os); 
166      } 
167   
168      //---------------------------------------------------------------------------- 
169      private void writeGraphicControlExtension(OutputStream os, boolean epluribus, 
170                                                int itransparent) throws IOException { 
171          int transflag = itransparent == -1 ? 0 : 1; 
172          if (transflag == 1 || epluribus)   // using transparency or animating ? 
173          { 
174              os.write((int) '!');             // GIF Extension Introducer 
175              os.write(0xf9);                  // Graphic Control Label 
176              os.write(4);                     // subsequent data block size 
177              os.write((disposalCode << 2) | transflag); // packed fields (1 byte) 
178              Put.leShort(csecsDelay, os);  // delay field (2 bytes) 
179              os.write(itransparent);          // transparent index field 
180              os.write(0);                     // block terminator 
181          } 
182      } 
183   
184      //---------------------------------------------------------------------------- 
185      private void writeImageDescriptor(OutputStream os) throws IOException { 
186          os.write((int) ',');                // Image Separator 
187          Put.leShort(thePosition.x, os); 
188          Put.leShort(thePosition.y, os); 
189          Put.leShort(theWidth, os); 
190          Put.leShort(theHeight, os); 
191          os.write(isInterlaced ? 0x40 : 0);  // packed fields (1 byte) 
192      } 
193  } 
194   
195  //============================================================================== 
196   
197  class GifPixelsEncoder { 
198   
199      private static final int EOF = -1; 
200   
201      private int imgW, imgH; 
202      private byte[] pixAry; 
203      private boolean wantInterlaced; 
204      private int initCodeSize; 
205   
206      // raster data navigators 
207      private int countDown; 
208      private int xCur, yCur; 
209      private int curPass; 
210   
211      //---------------------------------------------------------------------------- 
212      GifPixelsEncoder(int width, int height, byte[] pixels, boolean interlaced, 
213                       int color_depth) { 
214          imgW = width; 
215          imgH = height; 
216          pixAry = pixels; 
217          wantInterlaced = interlaced; 
218          initCodeSize = Math.max(2, color_depth); 
219      } 
220   
221      //---------------------------------------------------------------------------- 
222      void encode(OutputStream os) throws IOException { 
223          os.write(initCodeSize);         // write "initial code size" byte 
224   
225          countDown = imgW * imgH;        // reset navigation variables 
226          xCur = yCur = curPass = 0; 
227   
228          compress(initCodeSize + 1, os); // compress and write the pixel data 
229   
230          os.write(0);                    // write block terminator 
231      } 
232   
233      //**************************************************************************** 
234      // (J.E.) The logic of the next two methods is largely intact from 
235      // Jef Poskanzer.  Some stylistic changes were made for consistency sake, 
236      // plus the second method accesses the pixel value from a prefiltered linear 
237      // array.  That's about it. 
238      //**************************************************************************** 
239   
240      //---------------------------------------------------------------------------- 
241      // Bump the 'xCur' and 'yCur' to point to the next pixel. 
242      //---------------------------------------------------------------------------- 
243      private void bumpPosition() { 
244          // Bump the current X position 
245          ++xCur; 
246   
247          // If we are at the end of a scan line, set xCur back to the beginning 
248          // If we are interlaced, bump the yCur to the appropriate spot, 
249          // otherwise, just increment it. 
250          if (xCur == imgW) { 
251              xCur = 0; 
252   
253              if (!wantInterlaced) 
254                  ++yCur; 
255              else 
256                  switch (curPass) { 
257                      case 0: 
258                          yCur += 8; 
259                          if (yCur >= imgH) { 
260                              ++curPass; 
261                              yCur = 4; 
262                          } 
263                          break; 
264                      case 1: 
265                          yCur += 8; 
266                          if (yCur >= imgH) { 
267                              ++curPass; 
268                              yCur = 2; 
269                          } 
270                          break; 
271                      case 2: 
272                          yCur += 4; 
273                          if (yCur >= imgH) { 
274                              ++curPass; 
275                              yCur = 1; 
276                          } 
277                          break; 
278                      case 3: 
279                          yCur += 2; 
280                          break; 
281                  } 
282          } 
283      } 
284   
285      //---------------------------------------------------------------------------- 
286      // Return the next pixel from the image 
287      //---------------------------------------------------------------------------- 
288      private int nextPixel() { 
289          if (countDown == 0) 
290              return EOF; 
291   
292          --countDown; 
293   
294          byte pix = pixAry[yCur * imgW + xCur]; 
295   
296          bumpPosition(); 
297   
298          return pix & 0xff; 
299      } 
300   
301      //**************************************************************************** 
302      // (J.E.) I didn't touch Jef Poskanzer's code from this point on.  (Well, OK, 
303      // I changed the name of the sole outside method it accesses.)  I figure 
304      // if I have no idea how something works, I shouldn't play with it :) 
305      // 
306      // Despite its unencapsulated structure, this section is actually highly 
307      // self-contained.  The calling code merely calls compress(), and the present 
308      // code calls nextPixel() in the caller.  That's the sum total of their 
309      // communication.  I could have dumped it in a separate class with a callback 
310      // via an interface, but it didn't seem worth messing with. 
311      //**************************************************************************** 
312   
313      // GIFCOMPR.C       - GIF Image compression routines 
314      // 
315      // Lempel-Ziv compression based on 'compress'.  GIF modifications by 
316      // David Rowley (mgardi@watdcsu.waterloo.edu) 
317   
318      // General DEFINEs 
319   
320      static final int BITS = 12; 
321   
322      static final int HSIZE = 5003;                // 80% occupancy 
323   
324      // GIF Image compression - modified 'compress' 
325      // 
326      // Based on: compress.c - File compression ala IEEE Computer, June 1984. 
327      // 
328      // By Authors:  Spencer W. Thomas      (decvax!harpo!utah-cs!utah-gr!thomas) 
329      //              Jim McKie              (decvax!mcvax!jim) 
330      //              Steve Davies           (decvax!vax135!petsd!peora!srd) 
331      //              Ken Turkowski          (decvax!decwrl!turtlevax!ken) 
332      //              James A. Woods         (decvax!ihnp4!ames!jaw) 
333      //              Joe Orost              (decvax!vax135!petsd!joe) 
334   
335      int n_bits;                                // number of bits/code 
336      int maxbits = BITS;                        // user settable max # bits/code 
337      int maxcode;                        // maximum code, given n_bits 
338      int maxmaxcode = 1 << BITS; // should NEVER generate this code 
339   
340      final int MAXCODE(int n_bits) { 
341          return (1 << n_bits) - 1; 
342      } 
343   
344      int[] htab = new int[HSIZE]; 
345      int[] codetab = new int[HSIZE]; 
346   
347      int hsize = HSIZE;                // for dynamic table sizing 
348   
349      int free_ent = 0;                        // first unused entry 
350   
351      // block compression parameters -- after all codes are used up, 
352      // and compression rate changes, start over. 
353      boolean clear_flg = false; 
354   
355      // Algorithm:  use open addressing double hashing (no chaining) on the 
356      // prefix code / next character combination.  We do a variant of Knuth's 
357      // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime 
358      // secondary probe.  Here, the modular division first probe is gives way 
359      // to a faster exclusive-or manipulation.  Also do block compression with 
360      // an adaptive reset, whereby the code table is cleared when the compression 
361      // ratio decreases, but after the table fills.  The variable-length output 
362      // codes are re-sized at this point, and a special CLEAR code is generated 
363      // for the decompressor.  Late addition:  construct the table according to 
364      // file size for noticeable speed improvement on small files.  Please direct 
365      // questions about this implementation to ames!jaw. 
366   
367      int g_init_bits; 
368   
369      int ClearCode; 
370      int EOFCode; 
371   
372      void compress(int init_bits, OutputStream outs) throws IOException { 
373          int fcode; 
374          int i /* = 0 */; 
375          int c; 
376          int ent; 
377          int disp; 
378          int hsize_reg; 
379          int hshift; 
380   
381          // Set up the globals:  g_init_bits - initial number of bits 
382          g_init_bits = init_bits; 
383   
384          // Set up the necessary values 
385          clear_flg = false; 
386          n_bits = g_init_bits; 
387          maxcode = MAXCODE(n_bits); 
388   
389          ClearCode = 1 << (init_bits - 1); 
390          EOFCode = ClearCode + 1; 
391          free_ent = ClearCode + 2; 
392   
393          char_init(); 
394   
395          ent = nextPixel(); 
396   
397          hshift = 0; 
398          for (fcode = hsize; fcode < 65536; fcode *= 2) 
399              ++hshift; 
400          hshift = 8 - hshift;                        // set hash code range bound 
401   
402          hsize_reg = hsize; 
403          cl_hash(hsize_reg);        // clear hash table 
404   
405          output(ClearCode, outs); 
406   
407          outer_loop: 
408          while ((c = nextPixel()) != EOF) { 
409              fcode = (c << maxbits) + ent; 
410              i = (c << hshift) ^ ent;                // xor hashing 
411   
412              if (htab[i] == fcode) { 
413                  ent = codetab[i]; 
414                  continue; 
415              } else if (htab[i] >= 0)        // non-empty slot 
416              { 
417                  disp = hsize_reg - i;        // secondary hash (after G. Knott) 
418                  if (i == 0) 
419                      disp = 1; 
420                  do { 
421                      if ((i -= disp) < 0) 
422                          i += hsize_reg; 
423   
424                      if (htab[i] == fcode) { 
425                          ent = codetab[i]; 
426                          continue outer_loop; 
427                      } 
428                  } while (htab[i] >= 0); 
429              } 
430              output(ent, outs); 
431              ent = c; 
432              if (free_ent < maxmaxcode) { 
433                  codetab[i] = free_ent++;        // code -> hashtable 
434                  htab[i] = fcode; 
435              } else 
436                  cl_block(outs); 
437          } 
438          // Put out the final code. 
439          output(ent, outs); 
440          output(EOFCode, outs); 
441      } 
442   
443      // output 
444      // 
445      // Output the given code. 
446      // Inputs: 
447      //      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes 
448      //              that n_bits =< wordsize - 1. 
449      // Outputs: 
450      //      Outputs code to the file. 
451      // Assumptions: 
452      //      Chars are 8 bits long. 
453      // Algorithm: 
454      //      Maintain a BITS character long buffer (so that 8 codes will 
455      // fit in it exactly).  Use the VAX insv instruction to insert each 
456      // code in turn.  When the buffer fills up empty it and start over. 
457   
458      int cur_accum = 0; 
459      int cur_bits = 0; 
460   
461      int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 
462                     0x001F, 0x003F, 0x007F, 0x00FF, 
463                     0x01FF, 0x03FF, 0x07FF, 0x0FFF, 
464                     0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; 
465   
466      void output(int code, OutputStream outs) throws IOException { 
467          cur_accum &= masks[cur_bits]; 
468   
469          if (cur_bits > 0) 
470              cur_accum |= (code << cur_bits); 
471          else 
472              cur_accum = code; 
473   
474          cur_bits += n_bits; 
475   
476          while (cur_bits >= 8) { 
477              char_out((byte) (cur_accum & 0xff), outs); 
478              cur_accum >>= 8; 
479              cur_bits -= 8; 
480          } 
481   
482          // If the next entry is going to be too big for the code size, 
483          // then increase it, if possible. 
484          if (free_ent > maxcode || clear_flg) { 
485              if (clear_flg) { 
486                  maxcode = MAXCODE(n_bits = g_init_bits); 
487                  clear_flg = false; 
488              } else { 
489                  ++n_bits; 
490                  if (n_bits == maxbits) 
491                      maxcode = maxmaxcode; 
492                  else 
493                      maxcode = MAXCODE(n_bits); 
494              } 
495          } 
496   
497          if (code == EOFCode) { 
498              // At EOF, write the rest of the buffer. 
499              while (cur_bits > 0) { 
500                  char_out((byte) (cur_accum & 0xff), outs); 
501                  cur_accum >>= 8; 
502                  cur_bits -= 8; 
503              } 
504   
505              flush_char(outs); 
506          } 
507      } 
508   
509      // Clear out the hash table 
510   
511      // table clear for block compress 
512      void cl_block(OutputStream outs) throws IOException { 
513          cl_hash(hsize); 
514          free_ent = ClearCode + 2; 
515          clear_flg = true; 
516   
517          output(ClearCode, outs); 
518      } 
519   
520      // reset code table 
521      void cl_hash(int hsize) { 
522          for (int i = 0; i < hsize; ++i) 
523              htab[i] = -1; 
524      } 
525   
526      // GIF Specific routines 
527   
528      // Number of characters so far in this 'packet' 
529      int a_count; 
530   
531      // Set up the 'byte output' routine 
532      void char_init() { 
533          a_count = 0; 
534      } 
535   
536      // Define the storage for the packet accumulator 
537      byte[] accum = new byte[256]; 
538   
539      // Add a character to the end of the current packet, and if it is 254 
540      // characters, flush the packet to disk. 
541      void char_out(byte c, OutputStream outs) throws IOException { 
542          accum[a_count++] = c; 
543          if (a_count >= 254) 
544              flush_char(outs); 
545      } 
546   
547      // Flush the packet to disk, and reset the accumulator 
548      void flush_char(OutputStream outs) throws IOException { 
549          if (a_count > 0) { 
550              outs.write(a_count); 
551              outs.write(accum, 0, a_count); 
552              a_count = 0; 
553          } 
554      } 
555  }