J2ME GIF 解码器(支持动画)【转】
  vvb6bOG0D5hl 2023年11月19日 27 0


          作者: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("sample.gif");
  *    int n = d.getFrameCount();
  *    for (int i = 0; i < 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 = 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;
                     }
                     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或者移动开发的文章请访问

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月19日 0

暂无评论

推荐阅读
vvb6bOG0D5hl