gembin

OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

HBase, Hadoop, ZooKeeper, Cassandra

Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

About Me

 

GifDecoder.java源码(处理GIF图片)

 
 

import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;

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 BufferedInputStream 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 Rectangle lastRect; // last image rect
 protected BufferedImage image; // current frame
 protected BufferedImage 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 ArrayList frames; // frames read from current file
 protected int frameCount;

 static class GifFrame {
  public GifFrame(BufferedImage im, int del) {
   image = im;
   delay = del;
  }
  public BufferedImage 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.get(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 BufferedImage 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 =
   ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

  // 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) {
    int[] prev =
     ((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
    System.arraycopy(prev, 0, dest, 0, width * height);
    // copy pixels

    if (lastDispose == 2) {
     // fill last image rect area with background color
     Graphics2D g = image.createGraphics();
     Color c = null;
     if (transparency) {
      c = new Color(0, 0, 0, 0);  // assume background is transparent
     } else {
      c = new Color(lastBgColor); // use given background color
     }
     g.setColor(c);
     g.setComposite(AlphaComposite.Src); // replace area
     g.fill(lastRect);
     g.dispose();
    }
   }
  }

  // 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++;
    }
   }
  }
 }

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

 /**
  * Gets image size.
  *
  * @return GIF image dimensions
  */
 public Dimension getFrameSize() {
  return new Dimension(width, height);
 }

 /**
  * Reads GIF image from stream
  *
  * @param BufferedInputStream containing GIF file.
  * @return read status code (0 = no errors)
  */
 public int read(BufferedInputStream 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 (IOException e) {
  }
  return status;
 }

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

 /**
  * Reads GIF file from specified file/URL source  
  * (URL assumed if name contains ":/" or "file:")
  *
  * @param name String containing source
  * @return read status code (0 = no errors)
  */
 public int read(String name) {
  status = STATUS_OK;
  try {
   name = name.trim().toLowerCase();
   if ((name.indexOf("file:") >= 0) ||
    (name.indexOf(":/") > 0)) {
    URL url = new URL(name);
    in = new BufferedInputStream(url.openStream());
   } else {
    in = new BufferedInputStream(new FileInputStream(name));
   }
   status = read(in);
  } catch (IOException e) {
   status = STATUS_OPEN_ERROR;
  }

  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 ArrayList();
  gct = null;
  lct = null;
 }

 /**
  * Reads a single byte from the input stream.
  */
 protected int read() {
  int curByte = 0;
  try {
   curByte = in.read();
  } catch (IOException 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 (IOException e) {
   }

   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 (IOException e) {
  }
  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 =
   new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);

  setPixels(); // transfer pixel data to image

  frames.add(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;
  lastRect = new Rectangle(ix, iy, iw, ih);
  lastImage = image;
  lastBgColor = bgColor;
  int dispose = 0;
  boolean transparency = false;
  int delay = 0;
  lct = null;
 }


 protected void skip() {
  do {
   readBlock();
  } while ((blockSize > 0) && !err());
 }
}

posted on 2007-09-21 11:15 gembin 阅读(5349) 评论(2)  编辑  收藏

评论

# re: GifDecoder.java源码(处理GIF图片) 2007-09-22 00:13 千里冰封

果然很牛,不错  回复  更多评论   

# re: GifDecoder.java源码(处理GIF图片)[未登录] 2013-10-22 12:00 xxx

@千里冰封
傻逼  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(6)

随笔分类(440)

随笔档案(378)

文章档案(6)

新闻档案(1)

相册

收藏夹(9)

Adobe

Android

AS3

Blog-Links

Build

Design Pattern

Eclipse

Favorite Links

Flickr

Game Dev

HBase

Identity Management

IT resources

JEE

Language

OpenID

OSGi

SOA

Version Control

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

free counters