J2ME GIF 编码器(支持动画)


作者:hunhun1981
出自:http://blog.csdn.net/hunhun1981/

之前的文章简单介绍了一个J2ME的GIF解码器。

http://blog.csdn.net/hunhun1981/archive/2008/02/21/2112086.aspx

 其实,当时我只修改了很少的代码就让编码器也可以在J2ME环境下运行了,只是手机上可能会慢一些。

同时,以前有很多人讨论在J2ME中将绘制的内容保存为图片,保存到手机或者发送到网络。现在一般都采用了位图模式的png文件,或者直接就是像素的数据。看到这里大家可以发现,其实咱们也可以使用这个GIF编码器将内存中的图片对象,保存为GIF格式的图片,甚至是动画。

在今后的文章中还会在此基础上做一些优化,主要是针对屏幕尺寸,以及GIF文件的大小,做一下缩放。然后在解码器的基础上实现一个播放器。并且支持流的读取。

好了,废话不多说了,直接看代码吧!

 

/*  NeuQuant Neural-Net Quantization Algorithm
 * ------------------------------------------
 *
 * Copyright (c) 1994 Anthony Dekker
 *
 * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
 * See "Kohonen neural networks for optimal colour quantization"
 * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
 * for a discussion of the algorithm.
 *
 * Any party obtaining a copy of these files from the author, directly or
 * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
 * world-wide, paid up, royalty-free, nonexclusive right and license to deal
 * in this software and documentation files (the "Software"), including without
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons who receive
 * copies from any such party to do so, with the only requirement being
 * that this copyright notice remain intact.
 
*/

//  Ported to Java 12/00 K Weiner
public   class  NeuQuant {

    
protected   static   final   int  netsize  =   256 /*  number of colours used  */

    
/*  four primes near 500 - assume no image has a length so large  */
    
/*  that it is divisible by all four primes  */
    
protected   static   final   int  prime1  =   499 ;
    
protected   static   final   int  prime2  =   491 ;
    
protected   static   final   int  prime3  =   487 ;
    
protected   static   final   int  prime4  =   503 ;

    
protected   static   final   int  minpicturebytes  =  ( 3   *  prime4);
    
/*  minimum size for input image  */

    
/*
     * Program Skeleton ---------------- [select samplefac in range 1..30] [read
     * image from input file] pic = (unsigned char*) malloc(3*width*height);
     * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write
     * output image header, using writecolourmap(f)] inxbuild(); write output
     * image using inxsearch(b,g,r)
     
*/

    
/*
     * Network Definitions -------------------
     
*/

    
protected   static   final   int  maxnetpos  =  (netsize  -   1 );
    
protected   static   final   int  netbiasshift  =   4 /*  bias for colour values  */
    
protected   static   final   int  ncycles  =   100 /*  no. of learning cycles  */

    
/*  defs for freq and bias  */
    
protected   static   final   int  intbiasshift  =   16 /*  bias for fractions  */
    
protected   static   final   int  intbias  =  ((( int 1 <<  intbiasshift);
    
protected   static   final   int  gammashift  =   10 /*  gamma = 1024  */
    
protected   static   final   int  gamma  =  ((( int 1 <<  gammashift);
    
protected   static   final   int  betashift  =   10 ;
    
protected   static   final   int  beta  =  (intbias  >>  betashift);  /*  beta = 1/1024  */
    
protected   static   final   int  betagamma  =  (intbias  <<  (gammashift  -  betashift));

    
/*  defs for decreasing radius factor  */
    
protected   static   final   int  initrad  =  (netsize  >>   3 );  /*
                                                             * for 256 cols,
                                                             * radius starts
                                                             
*/
    
protected   static   final   int  radiusbiasshift  =   6 /*  at 32.0 biased by 6 bits  */
    
protected   static   final   int  radiusbias  =  ((( int 1 <<  radiusbiasshift);
    
protected   static   final   int  initradius  =  (initrad  *  radiusbias);  /*
                                                                     * and
                                                                     * decreases
                                                                     * by a
                                                                     
*/
    
protected   static   final   int  radiusdec  =   30 /*  factor of 1/30 each cycle  */

    
/*  defs for decreasing alpha factor  */
    
protected   static   final   int  alphabiasshift  =   10 /*  alpha starts at 1.0  */
    
protected   static   final   int  initalpha  =  ((( int 1 <<  alphabiasshift);

    
protected   int  alphadec;  /*  biased by 10 bits  */

    
/*  radbias and alpharadbias used for radpower calculation  */
    
protected   static   final   int  radbiasshift  =   8 ;
    
protected   static   final   int  radbias  =  ((( int 1 <<  radbiasshift);
    
protected   static   final   int  alpharadbshift  =  (alphabiasshift  +  radbiasshift);
    
protected   static   final   int  alpharadbias  =  ((( int 1 <<  alpharadbshift);

    
/*
     * Types and Global Variables --------------------------
     
*/

    
protected   byte [] thepicture;  /*  the input image itself  */
    
protected   int  lengthcount;  /*  lengthcount = H*W*3  */

    
protected   int  samplefac;  /*  sampling factor 1..30  */

    
//  typedef int pixel[4]; /* BGRc */
     protected   int [][] network;  /*  the network itself - [netsize][4]  */

    
protected   int [] netindex  =   new   int [ 256 ];
    
/*  for network lookup - really 256  */

    
protected   int [] bias  =   new   int [netsize];
    
/*  bias and freq arrays for learning  */
    
protected   int [] freq  =   new   int [netsize];
    
protected   int [] radpower  =   new   int [initrad];

    
/*  radpower for precomputation  */

    
/*
     * Initialise network in range (0,0,0) to (255,255,255) and set parameters
     * -----------------------------------------------------------------------
     
*/
    
public  NeuQuant( byte [] thepic,  int  len,  int  sample) {

        
int  i;
        
int [] p;

        thepicture 
=  thepic;
        lengthcount 
=  len;
        samplefac 
=  sample;

        network 
=   new   int [netsize][];
        
for  (i  =   0 ; i  <  netsize; i ++ ) {
            network[i] 
=   new   int [ 4 ];
            p 
=  network[i];
            p[
0 =  p[ 1 =  p[ 2 =  (i  <<  (netbiasshift  +   8 ))  /  netsize;
            freq[i] 
=  intbias  /  netsize;  /*  1/netsize  */
            bias[i] 
=   0 ;
        }
    }

    
public   byte [] colorMap() {
        
byte [] map  =   new   byte [ 3   *  netsize];
        
int [] index  =   new   int [netsize];
        
for  ( int  i  =   0 ; i  <  netsize; i ++ )
            index[network[i][
3 ]]  =  i;
        
int  k  =   0 ;
        
for  ( int  i  =   0 ; i  <  netsize; i ++ ) {
            
int  j  =  index[i];
            map[k
++ =  ( byte ) (network[j][ 0 ]);
            map[k
++ =  ( byte ) (network[j][ 1 ]);
            map[k
++ =  ( byte ) (network[j][ 2 ]);
        }
        
return  map;
    }

    
/*
     * Insertion sort of network and building of netindex[0..255] (to do after
     * unbias)
     * -------------------------------------------------------------------------------
     
*/
    
public   void  inxbuild() {

        
int  i, j, smallpos, smallval;
        
int [] p;
        
int [] q;
        
int  previouscol, startpos;

        previouscol 
=   0 ;
        startpos 
=   0 ;
        
for  (i  =   0 ; i  <  netsize; i ++ ) {
            p 
=  network[i];
            smallpos 
=  i;
            smallval 
=  p[ 1 ];  /*  index on g  */
            
/*  find smallest in i..netsize-1  */
            
for  (j  =  i  +   1 ; j  <  netsize; j ++ ) {
                q 
=  network[j];
                
if  (q[ 1 <  smallval) {  /*  index on g  */
                    smallpos 
=  j;
                    smallval 
=  q[ 1 ];  /*  index on g  */
                }
            }
            q 
=  network[smallpos];
            
/*  swap p (i) and q (smallpos) entries  */
            
if  (i  !=  smallpos) {
                j 
=  q[ 0 ];
                q[
0 =  p[ 0 ];
                p[
0 =  j;
                j 
=  q[ 1 ];
                q[
1 =  p[ 1 ];
                p[
1 =  j;
                j 
=  q[ 2 ];
                q[
2 =  p[ 2 ];
                p[
2 =  j;
                j 
=  q[ 3 ];
                q[
3 =  p[ 3 ];
                p[
3 =  j;
            }
            
/*  smallval entry is now in position i  */
            
if  (smallval  !=  previouscol) {
                netindex[previouscol] 
=  (startpos  +  i)  >>   1 ;
                
for  (j  =  previouscol  +   1 ; j  <  smallval; j ++ )
                    netindex[j] 
=  i;
                previouscol 
=  smallval;
                startpos 
=  i;
            }
        }
        netindex[previouscol] 
=  (startpos  +  maxnetpos)  >>   1 ;
        
for  (j  =  previouscol  +   1 ; j  <   256 ; j ++ )
            netindex[j] 
=  maxnetpos;  /*  really 256  */
    }

    
/*
     * Main Learning Loop ------------------
     
*/
    
public   void  learn() {

        
int  i, j, b, g, r;
        
int  radius, rad, alpha, step, delta, samplepixels;
        
byte [] p;
        
int  pix, lim;

        
if  (lengthcount  <  minpicturebytes)
            samplefac 
=   1 ;
        alphadec 
=   30   +  ((samplefac  -   1 /   3 );
        p 
=  thepicture;
        pix 
=   0 ;
        lim 
=  lengthcount;
        samplepixels 
=  lengthcount  /  ( 3   *  samplefac);
        delta 
=  samplepixels  /  ncycles;
        alpha 
=  initalpha;
        radius 
=  initradius;

        rad 
=  radius  >>  radiusbiasshift;
        
if  (rad  <=   1 )
            rad 
=   0 ;
        
for  (i  =   0 ; i  <  rad; i ++ )
            radpower[i] 
=  alpha
                    
*  (((rad  *  rad  -  i  *  i)  *  radbias)  /  (rad  *  rad));

        
//  fprintf(stderr,"beginning 1D learning: initial radius=%d ", rad);

        
if  (lengthcount  <  minpicturebytes)
            step 
=   3 ;
        
else   if  ((lengthcount  %  prime1)  !=   0 )
            step 
=   3   *  prime1;
        
else  {
            
if  ((lengthcount  %  prime2)  !=   0 )
                step 
=   3   *  prime2;
            
else  {
                
if  ((lengthcount  %  prime3)  !=   0 )
                    step 
=   3   *  prime3;
                
else
                    step 
=   3   *  prime4;
            }
        }

        i 
=   0 ;
        
while  (i  <  samplepixels) {
            b 
=  (p[pix  +   0 &   0xff <<  netbiasshift;
            g 
=  (p[pix  +   1 &   0xff <<  netbiasshift;
            r 
=  (p[pix  +   2 &   0xff <<  netbiasshift;
            j 
=  contest(b, g, r);

            altersingle(alpha, j, b, g, r);
            
if  (rad  !=   0 )
                alterneigh(rad, j, b, g, r); 
/*  alter neighbours  */

            pix 
+=  step;
            
if  (pix  >=  lim)
                pix 
-=  lengthcount;

            i
++ ;
            
if  (delta  ==   0 )
                delta 
=   1 ;
            
if  (i  %  delta  ==   0 ) {
                alpha 
-=  alpha  /  alphadec;
                radius 
-=  radius  /  radiusdec;
                rad 
=  radius  >>  radiusbiasshift;
                
if  (rad  <=   1 )
                    rad 
=   0 ;
                
for  (j  =   0 ; j  <  rad; j ++ )
                    radpower[j] 
=  alpha
                            
*  (((rad  *  rad  -  j  *  j)  *  radbias)  /  (rad  *  rad));
            }
        }
        
//  fprintf(stderr,"finished 1D learning: final alpha=%f
        
//  ! ",((float)alpha)/initalpha);
    }

    
/*
     * Search for BGR values 0..255 (after net is unbiased) and return colour
     * index
     * ----------------------------------------------------------------------------
     
*/
    
public   int  map( int  b,  int  g,  int  r) {

        
int  i, j, dist, a, bestd;
        
int [] p;
        
int  best;

        bestd 
=   1000 /*  biggest possible dist is 256*3  */
        best 
=   - 1 ;
        i 
=  netindex[g];  /*  index on g  */
        j 
=  i  -   1 /*  start at netindex[g] and work outwards  */

        
while  ((i  <  netsize)  ||  (j  >=   0 )) {
            
if  (i  <  netsize) {
                p 
=  network[i];
                dist 
=  p[ 1 -  g;  /*  inx key  */
                
if  (dist  >=  bestd)
                    i 
=  netsize;  /*  stop iter  */
                
else  {
                    i
++ ;
                    
if  (dist  <   0 )
                        dist 
=   - dist;
                    a 
=  p[ 0 -  b;
                    
if  (a  <   0 )
                        a 
=   - a;
                    dist 
+=  a;
                    
if  (dist  <  bestd) {
                        a 
=  p[ 2 -  r;
                        
if  (a  <   0 )
                            a 
=   - a;
                        dist 
+=  a;
                        
if  (dist  <  bestd) {
                            bestd 
=  dist;
                            best 
=  p[ 3 ];
                        }
                    }
                }
            }
            
if  (j  >=   0 ) {
                p 
=  network[j];
                dist 
=  g  -  p[ 1 ];  /*  inx key - reverse dif  */
                
if  (dist  >=  bestd)
                    j 
=   - 1 /*  stop iter  */
                
else  {
                    j
-- ;
                    
if  (dist  <   0 )
                        dist 
=   - dist;
                    a 
=  p[ 0 -  b;
                    
if  (a  <   0 )
                        a 
=   - a;
                    dist 
+=  a;
                    
if  (dist  <  bestd) {
                        a 
=  p[ 2 -  r;
                        
if  (a  <   0 )
                            a 
=   - a;
                        dist 
+=  a;
                        
if  (dist  <  bestd) {
                            bestd 
=  dist;
                            best 
=  p[ 3 ];
                        }
                    }
                }
            }
        }
        
return  (best);
    }

    
public   byte [] process() {
        learn();
        unbiasnet();
        inxbuild();
        
return  colorMap();
    }

    
/*
     * Unbias network to give byte values 0..255 and record position i to
     * prepare for sort
     * -----------------------------------------------------------------------------------
     
*/
    
public   void  unbiasnet() {

        
int  i, j;

        
for  (i  =   0 ; i  <  netsize; i ++ ) {
            network[i][
0 >>=  netbiasshift;
            network[i][
1 >>=  netbiasshift;
            network[i][
2 >>=  netbiasshift;
            network[i][
3 =  i;  /*  record colour no  */
        }
    }

    
/*
     * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in
     * radpower[|i-j|]
     * ---------------------------------------------------------------------------------
     
*/
    
protected   void  alterneigh( int  rad,  int  i,  int  b,  int  g,  int  r) {

        
int  j, k, lo, hi, a, m;
        
int [] p;

        lo 
=  i  -  rad;
        
if  (lo  <   - 1 )
            lo 
=   - 1 ;
        hi 
=  i  +  rad;
        
if  (hi  >  netsize)
            hi 
=  netsize;

        j 
=  i  +   1 ;
        k 
=  i  -   1 ;
        m 
=   1 ;
        
while  ((j  <  hi)  ||  (k  >  lo)) {
            a 
=  radpower[m ++ ];
            
if  (j  <  hi) {
                p 
=  network[j ++ ];
                
try  {
                    p[
0 -=  (a  *  (p[ 0 -  b))  /  alpharadbias;
                    p[
1 -=  (a  *  (p[ 1 -  g))  /  alpharadbias;
                    p[
2 -=  (a  *  (p[ 2 -  r))  /  alpharadbias;
                } 
catch  (Exception e) {
                } 
//  prevents 1.3 miscompilation
            }
            
if  (k  >  lo) {
                p 
=  network[k -- ];
                
try  {
                    p[
0 -=  (a  *  (p[ 0 -  b))  /  alpharadbias;
                    p[
1 -=  (a  *  (p[ 1 -  g))  /  alpharadbias;
                    p[
2 -=  (a  *  (p[ 2 -  r))  /  alpharadbias;
                } 
catch  (Exception e) {
                }
            }
        }
    }

    
/*
     * Move neuron i towards biased (b,g,r) by factor alpha
     * ----------------------------------------------------
     
*/
    
protected   void  altersingle( int  alpha,  int  i,  int  b,  int  g,  int  r) {

        
/*  alter hit neuron  */
        
int [] n  =  network[i];
        n[
0 -=  (alpha  *  (n[ 0 -  b))  /  initalpha;
        n[
1 -=  (alpha  *  (n[ 1 -  g))  /  initalpha;
        n[
2 -=  (alpha  *  (n[ 2 -  r))  /  initalpha;
    }

    
/*
     * Search for biased BGR values ----------------------------
     
*/
    
protected   int  contest( int  b,  int  g,  int  r) {

        
/*  finds closest neuron (min dist) and updates freq  */
        
/*  finds best neuron (min dist-bias) and returns position  */
        
/*
         * for frequently chosen neurons, freq[i] is high and bias[i] is
         * negative
         
*/
        
/*  bias[i] = gamma*((1/netsize)-freq[i])  */

        
int  i, dist, a, biasdist, betafreq;
        
int  bestpos, bestbiaspos, bestd, bestbiasd;
        
int [] n;

        bestd 
=   ~ ((( int 1 <<   31 );
        bestbiasd 
=  bestd;
        bestpos 
=   - 1 ;
        bestbiaspos 
=  bestpos;

        
for  (i  =   0 ; i  <  netsize; i ++ ) {
            n 
=  network[i];
            dist 
=  n[ 0 -  b;
            
if  (dist  <   0 )
                dist 
=   - dist;
            a 
=  n[ 1 -  g;
            
if  (a  <   0 )
                a 
=   - a;
            dist 
+=  a;
            a 
=  n[ 2 -  r;
            
if  (a  <   0 )
                a 
=   - a;
            dist 
+=  a;
            
if  (dist  <  bestd) {
                bestd 
=  dist;
                bestpos 
=  i;
            }
            biasdist 
=  dist  -  ((bias[i])  >>  (intbiasshift  -  netbiasshift));
            
if  (biasdist  <  bestbiasd) {
                bestbiasd 
=  biasdist;
                bestbiaspos 
=  i;
            }
            betafreq 
=  (freq[i]  >>  betashift);
            freq[i] 
-=  betafreq;
            bias[i] 
+=  (betafreq  <<  gammashift);
        }
        freq[bestpos] 
+=  beta;
        bias[bestpos] 
-=  betagamma;
        
return  (bestbiaspos);
    }
}

 

 

import  java.io.OutputStream;
import  java.io.IOException;

// ==============================================================================
//   Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
//   K Weiner 12/00

class  LZWEncoder {

    
private   static   final   int  EOF  =   - 1 ;

    
private   int  imgW, imgH;
    
private   byte [] pixAry;
    
private   int  initCodeSize;
    
private   int  remaining;
    
private   int  curPixel;

    
//  GIFCOMPR.C - GIF Image compression routines
    
//
    
//  Lempel-Ziv compression based on 'compress'. GIF modifications by
    
//  David Rowley (mgardi@watdcsu.waterloo.edu)

    
//  General DEFINEs

    
static   final   int  BITS  =   12 ;

    
static   final   int  HSIZE  =   5003 //  80% occupancy

    
//  GIF Image compression - modified 'compress'
    
//
    
//  Based on: compress.c - File compression ala IEEE Computer, June 1984.
    
//
    
//  By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
    
//  Jim McKie (decvax!mcvax!jim)
    
//  Steve Davies (decvax!vax135!petsd!peora!srd)
    
//  Ken Turkowski (decvax!decwrl!turtlevax!ken)
    
//  James A. Woods (decvax!ihnp4!ames!jaw)
    
//  Joe Orost (decvax!vax135!petsd!joe)

    
int  n_bits;  //  number of bits/code
     int  maxbits  =  BITS;  //  user settable max # bits/code
     int  maxcode;  //  maximum code, given n_bits
     int  maxmaxcode  =   1   <<  BITS;  //  should NEVER generate this code

    
int [] htab  =   new   int [HSIZE];
    
int [] codetab  =   new   int [HSIZE];

    
int  hsize  =  HSIZE;  //  for dynamic table sizing

    
int  free_ent  =   0 //  first unused entry

    
//  block compression parameters -- after all codes are used up,
    
//  and compression rate changes, start over.
     boolean  clear_flg  =   false ;

    
//  Algorithm: use open addressing double hashing (no chaining) on the
    
//  prefix code / next character combination. We do a variant of Knuth's
    
//  algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
    
//  secondary probe. Here, the modular division first probe is gives way
    
//  to a faster exclusive-or manipulation. Also do block compression with
    
//  an adaptive reset, whereby the code table is cleared when the compression
    
//  ratio decreases, but after the table fills. The variable-length output
    
//  codes are re-sized at this point, and a special CLEAR code is generated
    
//  for the decompressor. Late addition: construct the table according to
    
//  file size for noticeable speed improvement on small files. Please direct
    
//  questions about this implementation to ames!jaw.

    
int  g_init_bits;

    
int  ClearCode;
    
int  EOFCode;

    
//  output
    
//
    
//  Output the given code.
    
//  Inputs:
    
//  code: A n_bits-bit integer. If == -1, then EOF. This assumes
    
//  that n_bits =< wordsize - 1.
    
//  Outputs:
    
//  Outputs code to the file.
    
//  Assumptions:
    
//  Chars are 8 bits long.
    
//  Algorithm:
    
//  Maintain a BITS character long buffer (so that 8 codes will
    
//  fit in it exactly). Use the VAX insv instruction to insert each
    
//  code in turn. When the buffer fills up empty it and start over.

    
int  cur_accum  =   0 ;
    
int  cur_bits  =   0 ;

    
int  masks[]  =  {  0x0000 0x0001 0x0003 0x0007 0x000F 0x001F 0x003F ,
            
0x007F 0x00FF 0x01FF 0x03FF 0x07FF 0x0FFF 0x1FFF 0x3FFF ,
            
0x7FFF 0xFFFF  };

    
//  Number of characters so far in this 'packet'
     int  a_count;

    
//  Define the storage for the packet accumulator
     byte [] accum  =   new   byte [ 256 ];

    
//  ----------------------------------------------------------------------------
    LZWEncoder( int  width,  int  height,  byte [] pixels,  int  color_depth) {
        imgW 
=  width;
        imgH 
=  height;
        pixAry 
=  pixels;
        initCodeSize 
=  Math.max( 2 , color_depth);
    }

    
//  Add a character to the end of the current packet, and if it is 254
    
//  characters, flush the packet to disk.
     void  char_out( byte  c, OutputStream outs)  throws  IOException {
        accum[a_count
++ =  c;
        
if  (a_count  >=   254 )
            flush_char(outs);
    }

    
//  Clear out the hash table

    
//  table clear for block compress
     void  cl_block(OutputStream outs)  throws  IOException {
        cl_hash(hsize);
        free_ent 
=  ClearCode  +   2 ;
        clear_flg 
=   true ;

        output(ClearCode, outs);
    }

    
//  reset code table
     void  cl_hash( int  hsize) {
        
for  ( int  i  =   0 ; i  <  hsize;  ++ i)
            htab[i] 
=   - 1 ;
    }

    
void  compress( int  init_bits, OutputStream outs)  throws  IOException {
        
int  fcode;
        
int  i  /*  = 0  */ ;
        
int  c;
        
int  ent;
        
int  disp;
        
int  hsize_reg;
        
int  hshift;

        
//  Set up the globals: g_init_bits - initial number of bits
        g_init_bits  =  init_bits;

        
//  Set up the necessary values
        clear_flg  =   false ;
        n_bits 
=  g_init_bits;
        maxcode 
=  MAXCODE(n_bits);

        ClearCode 
=   1   <<  (init_bits  -   1 );
        EOFCode 
=  ClearCode  +   1 ;
        free_ent 
=  ClearCode  +   2 ;

        a_count 
=   0 //  clear packet

        ent 
=  nextPixel();

        hshift 
=   0 ;
        
for  (fcode  =  hsize; fcode  <   65536 ; fcode  *=   2 )
            
++ hshift;
        hshift 
=   8   -  hshift;  //  set hash code range bound

        hsize_reg 
=  hsize;
        cl_hash(hsize_reg); 
//  clear hash table

        output(ClearCode, outs);

        outer_loop: 
while  ((c  =  nextPixel())  !=  EOF) {
            fcode 
=  (c  <<  maxbits)  +  ent;
            i 
=  (c  <<  hshift)  ^  ent;  //  xor hashing

            
if  (htab[i]  ==  fcode) {
                ent 
=  codetab[i];
                
continue ;
            } 
else   if  (htab[i]  >=   0 //  non-empty slot
            {
                disp 
=  hsize_reg  -  i;  //  secondary hash (after G. Knott)
                 if  (i  ==   0 )
                    disp 
=   1 ;
                
do  {
                    
if  ((i  -=  disp)  <   0 )
                        i 
+=  hsize_reg;

                    
if  (htab[i]  ==  fcode) {
                        ent 
=  codetab[i];
                        
continue  outer_loop;
                    }
                } 
while  (htab[i]  >=   0 );
            }
            output(ent, outs);
            ent 
=  c;
            
if  (free_ent  <  maxmaxcode) {
                codetab[i] 
=  free_ent ++ //  code -> hashtable
                htab[i]  =  fcode;
            } 
else
                cl_block(outs);
        }
        
//  Put out the final code.
        output(ent, outs);
        output(EOFCode, outs);
    }

    
//  ----------------------------------------------------------------------------
     void  encode(OutputStream os)  throws  IOException {
        os.write(initCodeSize); 
//  write "initial code size" byte

        remaining 
=  imgW  *  imgH;  //  reset navigation variables
        curPixel  =   0 ;

        compress(initCodeSize 
+   1 , os);  //  compress and write the pixel data

        os.write(
0 );  //  write block terminator
    }

    
//  Flush the packet to disk, and reset the accumulator
     void  flush_char(OutputStream outs)  throws  IOException {
        
if  (a_count  >   0 ) {
            outs.write(a_count);
            outs.write(accum, 
0 , a_count);
            a_count 
=   0 ;
        }
    }

    
final   int  MAXCODE( int  n_bits) {
        
return  ( 1   <<  n_bits)  -   1 ;
    }

    
//  ----------------------------------------------------------------------------
    
//  Return the next pixel from the image
    
//  ----------------------------------------------------------------------------
     private   int  nextPixel() {
        
if  (remaining  ==   0 )
            
return  EOF;

        
-- remaining;

        
byte  pix  =  pixAry[curPixel ++ ];

        
return  pix  &   0xff ;
    }

    
void  output( int  code, OutputStream outs)  throws  IOException {
        cur_accum 
&=  masks[cur_bits];

        
if  (cur_bits  >   0 )
            cur_accum 
|=  (code  <<  cur_bits);
        
else
            cur_accum 
=  code;

        cur_bits 
+=  n_bits;

        
while  (cur_bits  >=   8 ) {
            char_out((
byte ) (cur_accum  &   0xff ), outs);
            cur_accum 
>>=   8 ;
            cur_bits 
-=   8 ;
        }

        
//  If the next entry is going to be too big for the code size,
        
//  then increase it, if possible.
         if  (free_ent  >  maxcode  ||  clear_flg) {
            
if  (clear_flg) {
                maxcode 
=  MAXCODE(n_bits  =  g_init_bits);
                clear_flg 
=   false ;
            } 
else  {
                
++ n_bits;
                
if  (n_bits  ==  maxbits)
                    maxcode 
=  maxmaxcode;
                
else
                    maxcode 
=  MAXCODE(n_bits);
            }
        }

        
if  (code  ==  EOFCode) {
            
//  At EOF, write the rest of the buffer.
             while  (cur_bits  >   0 ) {
                char_out((
byte ) (cur_accum  &   0xff ), outs);
                cur_accum 
>>=   8 ;
                cur_bits 
-=   8 ;
            }

            flush_char(outs);
        }
    }
}

 

 

import  java.io.IOException;
import  java.io.OutputStream;

import  javax.microedition.lcdui.Graphics;
import  javax.microedition.lcdui.Image;

/**
 * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or more
 * frames.
 * 
 * <pre>
 * Example:
 *    AnimatedGifEncoder e = new AnimatedGifEncoder();
 *    e.start(outputFileName);
 *    e.setDelay(1000);   // 1 frame per sec
 *    e.addFrame(image1);
 *    e.addFrame(image2);
 *    e.finish();
 * </pre>
 * 
 * No copyright asserted on the source code of this class. May be used for any
 * purpose, however, refer to the Unisys LZW patent for restrictions on use of
 * the associated LZWEncoder class. Please forward any corrections to
 * kweiner@fmsware.com.
 * 
 * 
@author  Kevin Weiner, FM Software
 * 
@version  1.03 November 2003
 * 
 
*/

public   class  AnimatedGifEncoder {

    
protected   int  width;  //  image size
     protected   int  height;
    
protected   int  transparent  =   0 //  transparent color if given
     protected   int  transIndex;  //  transparent index in color table
     protected   int  repeat  =   - 1 //  no repeat
     protected   int  delay  =   0 //  frame delay (hundredths)
     protected   boolean  started  =   false //  ready to output frames
     protected  OutputStream out;
    
protected  Image image;  //  current frame
     protected   byte [] pixels;  //  BGR byte array from frame
     protected   byte [] indexedPixels;  //  converted frame indexed to palette
     protected   int  colorDepth;  //  number of bit planes
     protected   byte [] colorTab;  //  RGB palette
     protected   boolean [] usedEntry  =   new   boolean [ 256 ];  //  active palette
    
//  entries
     protected   int  palSize  =   7 //  color table size (bits-1)
     protected   int  dispose  =   - 1 //  disposal code (-1 = use default)
     protected   boolean  closeStream  =   false //  close stream when finished
     protected   boolean  firstFrame  =   true ;
    
protected   boolean  sizeSet  =   false //  if false, get size from first frame
     protected   int  sample  =   10 //  default sample interval for quantizer

    
/**
     * Sets the delay time between each frame, or changes it for subsequent
     * frames (applies to last frame added).
     * 
     * 
@param  ms
     *            int delay time in milliseconds
     
*/
    
public   void  setDelay( int  ms) {
        delay 
=  ms;
    }

    
/**
     * Sets the GIF frame disposal code for the last added frame and any
     * subsequent frames. Default is 0 if no transparent color has been set,
     * otherwise 2.
     * 
     * 
@param  code
     *            int disposal code.
     
*/
    
public   void  setDispose( int  code) {
        
if  (code  >=   0 ) {
            dispose 
=  code;
        }
    }

    
/**
     * Sets the number of times the set of GIF frames should be played. Default
     * is 1; 0 means play indefinitely. Must be invoked before the first image
     * is added.
     * 
     * 
@param  iter
     *            int number of iterations.
     * 
@return
     
*/
    
public   void  setRepeat( int  iter) {
        
if  (iter  >=   0 ) {
            repeat 
=  iter;
        }
    }

    
/**
     * Sets the transparent color for the last added frame and any subsequent
     * frames. Since all colors are subject to modification in the quantization
     * process, the color in the final palette for each frame closest to the
     * given color becomes the transparent color for that frame. May be set to
     * null to indicate no transparent color.
     * 
     * 
@param  c
     *            Color to be treated as transparent on display.
     
*/
    
public   void  setTransparent( int  c) {
        transparent 
=  c;
    }

    
/**
     * Adds next GIF frame. The frame is not written immediately, but is
     * actually deferred until the next frame is received so that timing data
     * can be inserted. Invoking <code>finish()</code> flushes all frames. If
     * <code>setSize</code> was not invoked, the size of the first image is
     * used for all subsequent frames.
     * 
     * 
@param  im
     *            BufferedImage containing frame to write.
     * 
@return  true if successful.
     
*/
    
public   boolean  addFrame(Image im) {
        
if  ((im  ==   null ||   ! started) {
            
return   false ;
        }
        
boolean  ok  =   true ;
        
try  {
            
if  ( ! sizeSet) {
                
//  use first frame's size
                setSize(im.getWidth(), im.getHeight());
            }
            image 
=  im;
            getImagePixels(); 
//  convert to correct format if necessary
            analyzePixels();  //  build color table & map pixels
             if  (firstFrame) {
                writeLSD(); 
//  logical screen descriptior
                writePalette();  //  global color table
                 if  (repeat  >=   0 ) {
                    
//  use NS app extension to indicate reps
                    writeNetscapeExt();
                }
            }
            writeGraphicCtrlExt(); 
//  write graphic control extension
            writeImageDesc();  //  image descriptor
             if  ( ! firstFrame) {
                writePalette(); 
//  local color table
            }
            writePixels(); 
//  encode and write pixel data
            firstFrame  =   false ;
        } 
catch  (IOException e) {
            ok 
=   false ;
        }

        
return  ok;
    }

    
/**
     * Flushes any pending data and closes output file. If writing to an
     * OutputStream, the stream is not closed.
     
*/
    
public   boolean  finish() {
        
if  ( ! started)
            
return   false ;
        
boolean  ok  =   true ;
        started 
=   false ;
        
try  {
            out.write(
0x3b );  //  gif trailer
            out.flush();
            
if  (closeStream) {
                out.close();
            }
        } 
catch  (IOException e) {
            ok 
=   false ;
        }

        
//  reset for subsequent use
        transIndex  =   0 ;
        out 
=   null ;
        image 
=   null ;
        pixels 
=   null ;
        indexedPixels 
=   null ;
        colorTab 
=   null ;
        closeStream 
=   false ;
        firstFrame 
=   true ;

        
return  ok;
    }

    
/**
     * Sets quality of color quantization (conversion of images to the maximum
     * 256 colors allowed by the GIF specification). Lower values (minimum = 1)
     * produce better colors, but slow processing significantly. 10 is the
     * default, and produces good color mapping at reasonable speeds. Values
     * greater than 20 do not yield significant improvements in speed.
     * 
     * 
@param  quality
     *            int greater than 0.
     * 
@return
     
*/
    
public   void  setQuality( int  quality) {
        
if  (quality  <   1 )
            quality 
=   1 ;
        sample 
=  quality;
    }

    
/**
     * Sets the GIF frame size. The default size is the size of the first frame
     * added if this method is not invoked.
     * 
     * 
@param  w
     *            int frame width.
     * 
@param  h
     *            int frame width.
     
*/
    
public   void  setSize( int  w,  int  h) {
        
if  (started  &&   ! firstFrame)
            
return ;
        width 
=  w;
        height 
=  h;
        
if  (width  <   1 )
            width 
=   320 ;
        
if  (height  <   1 )
            height 
=   240 ;
        sizeSet 
=   true ;
    }

    
/**
     * Initiates GIF file creation on the given stream. The stream is not closed
     * automatically.
     * 
     * 
@param  os
     *            OutputStream on which GIF images are written.
     * 
@return  false if initial write failed.
     
*/
    
public   boolean  start(OutputStream os) {
        
if  (os  ==   null )
            
return   false ;
        
boolean  ok  =   true ;
        closeStream 
=   false ;
        out 
=  os;
        
try  {
            writeString(
" GIF89a " );  //  header
        }  catch  (IOException e) {
            ok 
=   false ;
        }
        
return  started  =  ok;
    }

    
/**
     * Analyzes image colors and creates color map.
     
*/
    
protected   void  analyzePixels() {
        
int  len  =  pixels.length;
        
int  nPix  =  len  /   3 ;
        indexedPixels 
=   new   byte [nPix];
        NeuQuant nq 
=   new  NeuQuant(pixels, len, sample);
        
//  initialize quantizer
        colorTab  =  nq.process();  //  create reduced palette
        
//  convert map from BGR to RGB
         for  ( int  i  =   0 ; i  <  colorTab.length; i  +=   3 ) {
            
byte  temp  =  colorTab[i];
            colorTab[i] 
=  colorTab[i  +   2 ];
            colorTab[i 
+   2 =  temp;
            usedEntry[i 
/   3 =   false ;
        }
        
//  map image pixels to new palette
         int  k  =   0 ;
        
for  ( int  i  =   0 ; i  <  nPix; i ++ ) {
            
int  index  =  nq.map(pixels[k ++ &   0xff , pixels[k ++ &   0xff ,
                    pixels[k
++ &   0xff );
            usedEntry[index] 
=   true ;
            indexedPixels[i] 
=  ( byte ) index;
        }
        pixels 
=   null ;
        colorDepth 
=   8 ;
        palSize 
=   7 ;
        
//  get closest match to transparent color if specified
         if  (transparent  !=   0 ) {
            transIndex 
=  transparent;
        }
    }

    
protected   int [] getImageData(Image img) {
        
int  w  =  img.getWidth();
        
int  h  =  img.getHeight();
        
int [] data  =   new   int [w  *  h];
        img.getRGB(data, 
0 , w,  0 0 , w, h);
        
return  data;
    }

    
/**
     * Extracts image pixels into byte array "pixels"
     
*/
    
protected   void  getImagePixels() {
        
int  w  =  image.getWidth();
        
int  h  =  image.getHeight();
        
if  ((w  !=  width)  ||  (h  !=  height)) {
            Image temp 
=  Image.createImage(width, height);
            Graphics g 
=  temp.getGraphics();
            g.drawImage(image, 
0 0 0 );
            image 
=  temp;
        }
        
int [] data  =  getImageData(image);
        pixels 
=   new   byte [data.length  *   3 ];
        
for  ( int  i  =   0 ; i  <  data.length; i ++ ) {
            
int  td  =  data[i];
            
int  tind  =  i  *   3 ;
            pixels[tind
++ =  ( byte ) ((td  >>   0 &   0xFF );
            pixels[tind
++ =  ( byte ) ((td  >>   8 &   0xFF );
            pixels[tind] 
=  ( byte ) ((td  >>   16 &   0xFF );
        }
    }

    
/**
     * Writes Graphic Control Extension
     
*/
    
protected   void  writeGraphicCtrlExt()  throws  IOException {
        out.write(
0x21 );  //  extension introducer
        out.write( 0xf9 );  //  GCE label
        out.write( 4 );  //  data block size
         int  transp, disp;
        
if  (transparent  ==   0 ) {
            transp 
=   0 ;
            disp 
=   0 //  dispose = no action
        }  else  {
            transp 
=   1 ;
            disp 
=   2 //  force clear if using transparent color
        }
        
if  (dispose  >=   0 ) {
            disp 
=  dispose  &   7 //  user override
        }
        disp 
<<=   2 ;

        
//  packed fields
        out.write( 0   |   //  1:3 reserved
                disp  |   //  4:6 disposal
                 0   |   //  7 user input - 0 = none
                transp);  //  8 transparency flag

        writeShort(delay); 
//  delay x 1/100 sec
        out.write(transIndex);  //  transparent color index
        out.write( 0 );  //  block terminator
    }

    
/**
     * Writes Image Descriptor
     
*/
    
protected   void  writeImageDesc()  throws  IOException {
        out.write(
0x2c );  //  image separator
        writeShort( 0 );  //  image position x,y = 0,0
        writeShort( 0 );
        writeShort(width); 
//  image size
        writeShort(height);
        
//  packed fields
         if  (firstFrame) {
            
//  no LCT - GCT is used for first (or only) frame
            out.write( 0 );
        } 
else  {
            
//  specify normal LCT
            out.write( 0x80   |   //  1 local color table 1=yes
                     0   |   //  2 interlace - 0=no
                     0   |   //  3 sorted - 0=no
                     0   |   //  4-5 reserved
                    palSize);  //  6-8 size of color table
        }
    }

    
/**
     * Writes Logical Screen Descriptor
     
*/
    
protected   void  writeLSD()  throws  IOException {
        
//  logical screen size
        writeShort(width);
        writeShort(height);
        
//  packed fields
        out.write(( 0x80   |   //  1 : global color table flag = 1 (gct used)
                 0x70   |   //  2-4 : color resolution = 7
                 0x00   |   //  5 : gct sort flag = 0
                palSize));  //  6-8 : gct size

        out.write(
0 );  //  background color index
        out.write( 0 );  //  pixel aspect ratio - assume 1:1
    }

    
/**
     * Writes Netscape application extension to define repeat count.
     
*/
    
protected   void  writeNetscapeExt()  throws  IOException {
        out.write(
0x21 );  //  extension introducer
        out.write( 0xff );  //  app extension label
        out.write( 11 );  //  block size
        writeString( " NETSCAPE "   +   " 2.0 " );  //  app id + auth code
        out.write( 3 );  //  sub-block size
        out.write( 1 );  //  loop sub-block id
        writeShort(repeat);  //  loop count (extra iterations, 0=repeat forever)
        out.write( 0 );  //  block terminator
    }

    
/**
     * Writes color table
     
*/
    
protected   void  writePalette()  throws  IOException {
        out.write(colorTab, 
0 , colorTab.length);
        
int  n  =  ( 3   *   256 -  colorTab.length;
        
for  ( int  i  =   0 ; i  <  n; i ++ ) {
            out.write(
0 );
        }
    }

    
/**
     * Encodes and writes pixel data
     
*/
    
protected   void  writePixels()  throws  IOException {
        LZWEncoder encoder 
=   new  LZWEncoder(width, height, indexedPixels,
                colorDepth);
        encoder.encode(out);
    }

    
/**
     * Write 16-bit value to output stream, LSB first
     
*/
    
protected   void  writeShort( int  value)  throws  IOException {
        out.write(value 
&   0xff );
        out.write((value 
>>   8 &   0xff );
    }

    
/**
     * Writes string to output stream
     
*/
    
protected   void  writeString(String s)  throws  IOException {
        
for  ( int  i  =   0 ; i  <  s.length(); i ++ ) {
            out.write((
byte ) s.charAt(i));
        }
    }
}

 

好了,就这么多,使用方法很简单,只需要仔细看看接口应该就能明白。大家自己尝试一下吧。

更多信息,请关注hunhun1981的专栏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值