J2ME GIF 解码器(支持动画)


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

之前转载了两篇关于在J2ME上进行GIF解码的文章。
Displaying GIF Images on J2ME Mobile Phones  
J2SE GIF Source 

第一篇文章讲解比较详细,曾被多次转载。而第二篇则是GIF编解码器的J2SE源代码。

我正是在参考了以上文章之后,才开始对该代码展开移植。包括测试也就用了半天时间。感兴趣的朋友最好自己动手试一试。有麻烦的话,可以联系我。

下面先把J2ME版本的GIF解码器源代码公布如下。稍后提供编码器的代码(工作比较忙,抱歉)。

 

import  java.io.InputStream;
import  java.util.Vector;

import  javax.microedition.lcdui.Image;

/**
 * Class GifDecoder - Decodes a GIF file into one or more frames. <br>
 * 
 * <pre>
 * Example:
 *    GifDecoder d = new GifDecoder();
 *    d.read(&quot;sample.gif&quot;);
 *    int n = d.getFrameCount();
 *    for (int i = 0; i &lt; n; i++) {
 *       BufferedImage frame = d.getFrame(i);  // frame i
 *       int t = d.getDelay(i);  // display duration of frame in milliseconds
 *       // do something with frame
 *    }
 * </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 any additional
 * restrictions. Please forward any corrections to kweiner@fmsware.com.
 * 
 * 
@author  Kevin Weiner, FM Software; LZW decoder adapted from John Cristy's
 *         ImageMagick.
 * 
@version  1.03 November 2003
 * 
 
*/

public   class  GifDecoder {

    
/**
     * File read status: No errors.
     
*/
    
public   static   final   int  STATUS_OK  =   0 ;

    
/**
     * File read status: Error decoding file (may be partially decoded)
     
*/
    
public   static   final   int  STATUS_FORMAT_ERROR  =   1 ;

    
/**
     * File read status: Unable to open source.
     
*/
    
public   static   final   int  STATUS_OPEN_ERROR  =   2 ;

    
protected  InputStream in;
    
protected   int  status;

    
protected   int  width;  //  full image width
     protected   int  height;  //  full image height
     protected   boolean  gctFlag;  //  global color table used
     protected   int  gctSize;  //  size of global color table
     protected   int  loopCount  =   1 //  iterations; 0 = repeat forever

    
protected   int [] gct;  //  global color table
     protected   int [] lct;  //  local color table
     protected   int [] act;  //  active color table

    
protected   int  bgIndex;  //  background color index
     protected   int  bgColor;  //  background color
     protected   int  lastBgColor;  //  previous bg color
     protected   int  pixelAspect;  //  pixel aspect ratio

    
protected   boolean  lctFlag;  //  local color table flag
     protected   boolean  interlace;  //  interlace flag
     protected   int  lctSize;  //  local color table size

    
protected   int  ix, iy, iw, ih;  //  current image rectangle
     protected   int  lrx, lry, lrw, lrh;
    
protected  Image image;  //  current frame
     protected  Image lastImage;  //  previous frame

    
protected   byte [] block  =   new   byte [ 256 ];  //  current data block
     protected   int  blockSize  =   0 //  block size

    
//  last graphic control extension info
     protected   int  dispose  =   0 ;
    
//  0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
     protected   int  lastDispose  =   0 ;
    
protected   boolean  transparency  =   false //  use transparent color
     protected   int  delay  =   0 //  delay in milliseconds
     protected   int  transIndex;  //  transparent color index

    
protected   static   final   int  MaxStackSize  =   4096 ;
    
//  max decoder pixel stack size

    
//  LZW decoder working arrays
     protected   short [] prefix;
    
protected   byte [] suffix;
    
protected   byte [] pixelStack;
    
protected   byte [] pixels;

    
protected  Vector frames;  //  frames read from current file
     protected   int  frameCount;

    
static   class  GifFrame {
        
public  GifFrame(Image im,  int  del) {
            image 
=  im;
            delay 
=  del;
        }

        
public  Image image;
        
public   int  delay;
    }

    
/**
     * Gets display duration for specified frame.
     * 
     * 
@param  n
     *            int index of frame
     * 
@return  delay in milliseconds
     
*/
    
public   int  getDelay( int  n) {
        delay 
=   - 1 ;
        
if  ((n  >=   0 &&  (n  <  frameCount)) {
            delay 
=  ((GifFrame) frames.elementAt(n)).delay;
        }
        
return  delay;
    }

    
/**
     * Gets the number of frames read from file.
     * 
     * 
@return  frame count
     
*/
    
public   int  getFrameCount() {
        
return  frameCount;
    }

    
/**
     * Gets the first (or only) image read.
     * 
     * 
@return  BufferedImage containing first frame, or null if none.
     
*/
    
public  Image getImage() {
        
return  getFrame( 0 );
    }

    
/**
     * Gets the "Netscape" iteration count, if any. A count of 0 means repeat
     * indefinitiely.
     * 
     * 
@return  iteration count if one was specified, else 1.
     
*/
    
public   int  getLoopCount() {
        
return  loopCount;
    }

    
/**
     * Creates new frame image from current data (and previous frames as
     * specified by their disposition codes).
     
*/
    
protected   void  setPixels() {
        
//  expose destination image's pixels as int array
         int [] dest  =   new   int [width  *  height];

        
//  fill in starting image contents based on last image's dispose code
         if  (lastDispose  >   0 ) {
            
if  (lastDispose  ==   3 ) {
                
//  use image before last
                 int  n  =  frameCount  -   2 ;
                
if  (n  >   0 ) {
                    lastImage 
=  getFrame(n  -   1 );
                } 
else  {
                    lastImage 
=   null ;
                }
            }

            
if  (lastImage  !=   null ) {
                lastImage.getRGB(dest, 
0 , width,  0 0 , width, height);
                
//  copy pixels

                
if  (lastDispose  ==   2 ) {
                    
//  fill last image rect area with background color
                     int  c  =   0 ;
                    
if  ( ! transparency) {
                        c 
=  lastBgColor;
                    }
                    
for  ( int  i  =   0 ; i  <  lrh; i ++ ) {
                        
int  n1  =  (lry  +  i)  *  width  +  lrx;
                        
int  n2  =  n1  +  lrw;
                        
for  ( int  k  =  n1; k  <  n2; k ++ ) {
                            dest[k] 
=  c;
                        }
                    }
                }
            }
        }

        
//  copy each source line to the appropriate place in the destination
         int  pass  =   1 ;
        
int  inc  =   8 ;
        
int  iline  =   0 ;
        
for  ( int  i  =   0 ; i  <  ih; i ++ ) {
            
int  line  =  i;
            
if  (interlace) {
                
if  (iline  >=  ih) {
                    pass
++ ;
                    
switch  (pass) {
                    
case   2 :
                        iline 
=   4 ;
                        
break ;
                    
case   3 :
                        iline 
=   2 ;
                        inc 
=   4 ;
                        
break ;
                    
case   4 :
                        iline 
=   1 ;
                        inc 
=   2 ;
                    }
                }
                line 
=  iline;
                iline 
+=  inc;
            }
            line 
+=  iy;
            
if  (line  <  height) {
                
int  k  =  line  *  width;
                
int  dx  =  k  +  ix;  //  start of line in dest
                 int  dlim  =  dx  +  iw;  //  end of dest line
                 if  ((k  +  width)  <  dlim) {
                    dlim 
=  k  +  width;  //  past dest edge
                }
                
int  sx  =  i  *  iw;  //  start of line in source
                 while  (dx  <  dlim) {
                    
//  map color and insert in destination
                     int  index  =  (( int ) pixels[sx ++ ])  &   0xff ;
                    
int  c  =  act[index];
                    
if  (c  !=   0 ) {
                        dest[dx] 
=  c;
                    }
                    dx
++ ;
                }
            }
        }
        image 
=  Image.createRGBImage(dest, width, height,  false );
    }

    
/**
     * Gets the image contents of frame n.
     * 
     * 
@return  BufferedImage representation of frame, or null if n is invalid.
     
*/
    
public  Image getFrame( int  n) {
        Image im 
=   null ;
        
if  ((n  >=   0 &&  (n  <  frameCount)) {
            im 
=  ((GifFrame) frames.elementAt(n)).image;
        }
        
return  im;
    }

    
/**
     * Reads GIF image from stream
     * 
     * 
@param  BufferedInputStream
     *            containing GIF file.
     * 
@return  read status code (0 = no errors)
     
*/
    
public   int  read(InputStream is) {
        init();
        
if  (is  !=   null ) {
            in 
=  is;
            readHeader();
            
if  ( ! err()) {
                readContents();
                
if  (frameCount  <   0 ) {
                    status 
=  STATUS_FORMAT_ERROR;
                }
            }
        } 
else  {
            status 
=  STATUS_OPEN_ERROR;
        }
        
try  {
            is.close();
        } 
catch  (Exception e) {
            e.printStackTrace();
        }
        
return  status;
    }

    
/**
     * Decodes LZW image data into pixel array. Adapted from John Cristy's
     * ImageMagick.
     
*/
    
protected   void  decodeImageData() {
        
int  NullCode  =   - 1 ;
        
int  npix  =  iw  *  ih;
        
int  available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;

        
if  ((pixels  ==   null ||  (pixels.length  <  npix)) {
            pixels 
=   new   byte [npix];  //  allocate new pixel array
        }
        
if  (prefix  ==   null )
            prefix 
=   new   short [MaxStackSize];
        
if  (suffix  ==   null )
            suffix 
=   new   byte [MaxStackSize];
        
if  (pixelStack  ==   null )
            pixelStack 
=   new   byte [MaxStackSize  +   1 ];

        
//  Initialize GIF data stream decoder.

        data_size 
=  read();
        clear 
=   1   <<  data_size;
        end_of_information 
=  clear  +   1 ;
        available 
=  clear  +   2 ;
        old_code 
=  NullCode;
        code_size 
=  data_size  +   1 ;
        code_mask 
=  ( 1   <<  code_size)  -   1 ;
        
for  (code  =   0 ; code  <  clear; code ++ ) {
            prefix[code] 
=   0 ;
            suffix[code] 
=  ( byte ) code;
        }

        
//  Decode GIF pixel stream.

        datum 
=  bits  =  count  =  first  =  top  =  pi  =  bi  =   0 ;

        
for  (i  =   0 ; i  <  npix;) {
            
if  (top  ==   0 ) {
                
if  (bits  <  code_size) {
                    
//  Load bytes until there are enough bits for a code.
                     if  (count  ==   0 ) {
                        
//  Read a new data block.
                        count  =  readBlock();
                        
if  (count  <=   0 )
                            
break ;
                        bi 
=   0 ;
                    }
                    datum 
+=  ((( int ) block[bi])  &   0xff <<  bits;
                    bits 
+=   8 ;
                    bi
++ ;
                    count
-- ;
                    
continue ;
                }

                
//  Get the next code.

                code 
=  datum  &  code_mask;
                datum 
>>=  code_size;
                bits 
-=  code_size;

                
//  Interpret the code

                
if  ((code  >  available)  ||  (code  ==  end_of_information))
                    
break ;
                
if  (code  ==  clear) {
                    
//  Reset decoder.
                    code_size  =  data_size  +   1 ;
                    code_mask 
=  ( 1   <<  code_size)  -   1 ;
                    available 
=  clear  +   2 ;
                    old_code 
=  NullCode;
                    
continue ;
                }
                
if  (old_code  ==  NullCode) {
                    pixelStack[top
++ =  suffix[code];
                    old_code 
=  code;
                    first 
=  code;
                    
continue ;
                }
                in_code 
=  code;
                
if  (code  ==  available) {
                    pixelStack[top
++ =  ( byte ) first;
                    code 
=  old_code;
                }
                
while  (code  >  clear) {
                    pixelStack[top
++ =  suffix[code];
                    code 
=  prefix[code];
                }
                first 
=  (( int ) suffix[code])  &   0xff ;

                
//  Add a new string to the string table,

                
if  (available  >=  MaxStackSize)
                    
break ;
                pixelStack[top
++ =  ( byte ) first;
                prefix[available] 
=  ( short ) old_code;
                suffix[available] 
=  ( byte ) first;
                available
++ ;
                
if  (((available  &  code_mask)  ==   0 )
                        
&&  (available  <  MaxStackSize)) {
                    code_size
++ ;
                    code_mask 
+=  available;
                }
                old_code 
=  in_code;
            }

            
//  Pop a pixel off the pixel stack.

            top
-- ;
            pixels[pi
++ =  pixelStack[top];
            i
++ ;
        }

        
for  (i  =  pi; i  <  npix; i ++ ) {
            pixels[i] 
=   0 //  clear missing pixels
        }

    }

    
/**
     * Returns true if an error was encountered during reading/decoding
     
*/
    
protected   boolean  err() {
        
return  status  !=  STATUS_OK;
    }

    
/**
     * Initializes or re-initializes reader
     
*/
    
protected   void  init() {
        status 
=  STATUS_OK;
        frameCount 
=   0 ;
        frames 
=   new  Vector();
        gct 
=   null ;
        lct 
=   null ;
    }

    
/**
     * Reads a single byte from the input stream.
     
*/
    
protected   int  read() {
        
int  curByte  =   0 ;
        
try  {
            curByte 
=  in.read();
        } 
catch  (Exception e) {
            status 
=  STATUS_FORMAT_ERROR;
        }
        
return  curByte;
    }

    
/**
     * Reads next variable length block from input.
     * 
     * 
@return  number of bytes stored in "buffer"
     
*/
    
protected   int  readBlock() {
        blockSize 
=  read();
        
int  n  =   0 ;
        
if  (blockSize  >   0 ) {
            
try  {
                
int  count  =   0 ;
                
while  (n  <  blockSize) {
                    count 
=  in.read(block, n, blockSize  -  n);
                    
if  (count  ==   - 1 )
                        
break ;
                    n 
+=  count;
                }
            } 
catch  (Exception e) {
                e.printStackTrace();
            }

            
if  (n  <  blockSize) {
                status 
=  STATUS_FORMAT_ERROR;
            }
        }
        
return  n;
    }

    
/**
     * Reads color table as 256 RGB integer values
     * 
     * 
@param  ncolors
     *            int number of colors to read
     * 
@return  int array containing 256 colors (packed ARGB with full alpha)
     
*/
    
protected   int [] readColorTable( int  ncolors) {
        
int  nbytes  =   3   *  ncolors;
        
int [] tab  =   null ;
        
byte [] c  =   new   byte [nbytes];
        
int  n  =   0 ;
        
try  {
            n 
=  in.read(c);
        } 
catch  (Exception e) {
            e.printStackTrace();
        }
        
if  (n  <  nbytes) {
            status 
=  STATUS_FORMAT_ERROR;
        } 
else  {
            tab 
=   new   int [ 256 ];  //  max size to avoid bounds checks
             int  i  =   0 ;
            
int  j  =   0 ;
            
while  (i  <  ncolors) {
                
int  r  =  (( int ) c[j ++ ])  &   0xff ;
                
int  g  =  (( int ) c[j ++ ])  &   0xff ;
                
int  b  =  (( int ) c[j ++ ])  &   0xff ;
                tab[i
++ =   0xff000000   |  (r  <<   16 |  (g  <<   8 |  b;
            }
        }
        
return  tab;
    }

    
/**
     * Main file parser. Reads GIF content blocks.
     
*/
    
protected   void  readContents() {
        
//  read GIF file content blocks
         boolean  done  =   false ;
        
while  ( ! (done  ||  err())) {
            
int  code  =  read();
            
switch  (code) {

            
case   0x2C //  image separator
                readImage();
                
break ;

            
case   0x21 //  extension
                code  =  read();
                
switch  (code) {
                
case   0xf9 //  graphics control extension
                    readGraphicControlExt();
                    
break ;

                
case   0xff //  application extension
                    readBlock();
                    String app 
=   "" ;
                    
for  ( int  i  =   0 ; i  <   11 ; i ++ ) {
                        app 
+=  ( char ) block[i];
                    }
                    
if  (app.equals( " NETSCAPE2.0 " )) {
                        readNetscapeExt();
                    } 
else
                        skip(); 
//  don't care
                     break ;

                
default //  uninteresting extension
                    skip();
                }
                
break ;

            
case   0x3b //  terminator
                done  =   true ;
                
break ;

            
case   0x00 //  bad byte, but keep going and see what happens
                 break ;

            
default :
                status 
=  STATUS_FORMAT_ERROR;
            }
        }
    }

    
/**
     * Reads Graphics Control Extension values
     
*/
    
protected   void  readGraphicControlExt() {
        read(); 
//  block size
         int  packed  =  read();  //  packed fields
        dispose  =  (packed  &   0x1c >>   2 //  disposal method
         if  (dispose  ==   0 ) {
            dispose 
=   1 //  elect to keep old image if discretionary
        }
        transparency 
=  (packed  &   1 !=   0 ;
        delay 
=  readShort()  *   10 //  delay in milliseconds
        transIndex  =  read();  //  transparent color index
        read();  //  block terminator
    }

    
/**
     * Reads GIF file header information.
     
*/
    
protected   void  readHeader() {
        String id 
=   "" ;
        
for  ( int  i  =   0 ; i  <   6 ; i ++ ) {
            id 
+=  ( char ) read();
        }
        
if  ( ! id.startsWith( " GIF " )) {
            status 
=  STATUS_FORMAT_ERROR;
            
return ;
        }

        readLSD();
        
if  (gctFlag  &&   ! err()) {
            gct 
=  readColorTable(gctSize);
            bgColor 
=  gct[bgIndex];
        }
    }

    
/**
     * Reads next frame image
     
*/
    
protected   void  readImage() {
        ix 
=  readShort();  //  (sub)image position & size
        iy  =  readShort();
        iw 
=  readShort();
        ih 
=  readShort();

        
int  packed  =  read();
        lctFlag 
=  (packed  &   0x80 !=   0 //  1 - local color table flag
        interlace  =  (packed  &   0x40 !=   0 //  2 - interlace flag
        
//  3 - sort flag
        
//  4-5 - reserved
        lctSize  =   2   <<  (packed  &   7 );  //  6-8 - local color table size

        
if  (lctFlag) {
            lct 
=  readColorTable(lctSize);  //  read table
            act  =  lct;  //  make local table active
        }  else  {
            act 
=  gct;  //  make global table active
             if  (bgIndex  ==  transIndex)
                bgColor 
=   0 ;
        }
        
int  save  =   0 ;
        
if  (transparency) {
            save 
=  act[transIndex];
            act[transIndex] 
=   0 //  set transparent color if specified
        }

        
if  (act  ==   null ) {
            status 
=  STATUS_FORMAT_ERROR;  //  no color table defined
        }

        
if  (err())
            
return ;

        decodeImageData(); 
//  decode pixel data
        skip();

        
if  (err())
            
return ;

        frameCount
++ ;

        
//  create new image to receive frame data
        image  =  Image.createImage(width, height);

        setPixels(); 
//  transfer pixel data to image

        frames.addElement(
new  GifFrame(image, delay));  //  add image to frame
        
//  list

        
if  (transparency) {
            act[transIndex] 
=  save;
        }
        resetFrame();

    }

    
/**
     * Reads Logical Screen Descriptor
     
*/
    
protected   void  readLSD() {

        
//  logical screen size
        width  =  readShort();
        height 
=  readShort();

        
//  packed fields
         int  packed  =  read();
        gctFlag 
=  (packed  &   0x80 !=   0 //  1 : global color table flag
        
//  2-4 : color resolution
        
//  5 : gct sort flag
        gctSize  =   2   <<  (packed  &   7 );  //  6-8 : gct size

        bgIndex 
=  read();  //  background color index
        pixelAspect  =  read();  //  pixel aspect ratio
    }

    
/**
     * Reads Netscape extenstion to obtain iteration count
     
*/
    
protected   void  readNetscapeExt() {
        
do  {
            readBlock();
            
if  (block[ 0 ==   1 ) {
                
//  loop count sub-block
                 int  b1  =  (( int ) block[ 1 ])  &   0xff ;
                
int  b2  =  (( int ) block[ 2 ])  &   0xff ;
                loopCount 
=  (b2  <<   8 |  b1;
            }
        } 
while  ((blockSize  >   0 &&   ! err());
    }

    
/**
     * Reads next 16-bit value, LSB first
     
*/
    
protected   int  readShort() {
        
//  read 16-bit value, LSB first
         return  read()  |  (read()  <<   8 );
    }

    
/**
     * Resets frame state for reading next image.
     
*/
    
protected   void  resetFrame() {
        lastDispose 
=  dispose;
        lrx 
=  ix;
        lry 
=  iy;
        lrw 
=  iw;
        lrh 
=  ih;
        lastImage 
=  image;
        lastBgColor 
=  bgColor;
        dispose 
=   0 ;
        transparency 
=   false ;
        delay 
=   0 ;
        lct 
=   null ;
    }

    
/**
     * Skips variable length blocks up to and including next zero length block.
     
*/
    
protected   void  skip() {
        
do  {
            readBlock();
        } 
while  ((blockSize  >   0 &&   ! err());
    }
}

 

使用起来比较简单,可以参考如下代码

 

import  java.io.ByteArrayInputStream;
import  java.io.ByteArrayOutputStream;

import  javax.microedition.lcdui.Canvas;
import  javax.microedition.lcdui.Display;
import  javax.microedition.lcdui.Graphics;
import  javax.microedition.lcdui.Image;
import  javax.microedition.midlet.MIDlet;

public   class  gifme  extends  MIDlet {

    
private  gifCanvas canvas;

    
public  gifme() {
        canvas 
=   new  gifCanvas();
    }

    
protected   void  destroyApp( boolean  arg0) {

    }

    
protected   void  pauseApp() {

    }

    
protected   void  startApp() {
        Display.getDisplay(
this ).setCurrent(canvas);
        
new  Thread(canvas).start();
    }

    
public   class  gifCanvas  extends  Canvas  implements  Runnable {

        
private  GifDecoder d;
        
private   int  ind;
        
private   int  count;
        
private   long  delay;
        
private  Image frame;

        
private   long  start;

        
public  gifCanvas() {
            
long  start  =  System.currentTimeMillis();
            d 
=   new  GifDecoder();
            d.read(getClass().getResourceAsStream(
" /rr.gif " ));

            System.out.println(System.currentTimeMillis()  -  start);
            ind 
=   0 ;
            count 
=  d.getFrameCount();
        }

         public   void  run() {
            
while  ( true ) {
                
long  now  =  System.currentTimeMillis();
                
if  (now  -  start  >=  delay) {
                    repaint();
                    
try  {
                        Thread.sleep(delay);
                    } 
catch  (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        
public   void  paint(Graphics g) {
            start 
=  System.currentTimeMillis();
            frame 
=  d.getFrame(ind);
            delay 
=  d.getDelay(ind);
            System.out.println(
" frame =  "   +  ind  +   "   delay =  "   +  delay);
            ind
++ ;
            
if  (ind  >=  count) {
                ind 
=   0 ;
            }
            g.drawImage(frame, 
0 0 0 );
        }
    }
}

 

之前在搜索引擎上发现,有的网站有恶意转载的行为,在此对这种行为表示谴责。

请尊重别人的劳动。更多J2ME或者移动开发的文章请访问http://blog.csdn.net/hunhun1981/

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值