📄 gifdecoder.cs
字号:
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace LanMsg.Gif.Components
{
public class GifDecoder
{
/**
* File read status: No errors.
*/
public static readonly int STATUS_OK = 0;
/**
* File read status: Error decoding file (may be partially decoded)
*/
public static readonly int STATUS_FORMAT_ERROR = 1;
/**
* File read status: Unable to open source.
*/
public static readonly int STATUS_OPEN_ERROR = 2;
protected Stream inStream;
protected int status;
protected int width; // full image width
protected int height; // full image height
protected bool 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 bool lctFlag; // local color table flag
protected bool 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 Image image; // current frame
protected Bitmap bitmap;
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 bool transparency = false; // use transparent color
protected int delay = 0; // delay in milliseconds
protected int transIndex; // transparent color index
protected static readonly 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;
public 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[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).
*/
int [] GetPixels( Bitmap bitmap )
{
int [] pixels = new int [ 3 * image.Width * image.Height ];
int count = 0;
for (int th = 0; th < image.Height; th++)
{
for (int tw = 0; tw < image.Width; tw++)
{
Color color = bitmap.GetPixel(tw, th);
pixels[count] = color.R;
count++;
pixels[count] = color.G;
count++;
pixels[count] = color.B;
count++;
}
}
return pixels;
}
void SetPixels( int [] pixels )
{
int count = 0;
for (int th = 0; th < image.Height; th++)
{
for (int tw = 0; tw < image.Width; tw++)
{
Color color = Color.FromArgb( pixels[count++] );
bitmap.SetPixel( tw, th, color );
}
}
}
protected void SetPixels()
{
// expose destination image's pixels as int array
// int[] dest =
// (( int ) image.getRaster().getDataBuffer()).getData();
int[] dest = GetPixels( bitmap );
// 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();
int[] prev = GetPixels( new Bitmap( lastImage ) );
Array.Copy(prev, 0, dest, 0, width * height);
// copy pixels
if (lastDispose == 2)
{
// fill last image rect area with background color
Graphics g = Graphics.FromImage( image );
Color c = Color.Empty;
if (transparency)
{
c = Color.FromArgb( 0, 0, 0, 0 ); // assume background is transparent
}
else
{
c = Color.FromArgb( lastBgColor ) ;
// c = new Color(lastBgColor); // use given background color
}
Brush brush = new SolidBrush( c );
g.FillRectangle( brush, lastRect );
brush.Dispose();
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;
break;
}
}
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++;
}
}
}
SetPixels( dest );
}
/**
* 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[n] ).image;
}
return im;
}
/**
* Gets image size.
*
* @return GIF image dimensions
*/
public Size GetFrameSize()
{
return new Size(width, height);
}
/**
* Reads GIF image from stream
*
* @param BufferedInputStream containing GIF file.
* @return read status code (0 = no errors)
*/
public int Read( Stream inStream )
{
Init();
if ( inStream != null)
{
this.inStream = inStream;
ReadHeader();
if (!Error())
{
ReadContents();
if (frameCount < 0)
{
status = STATUS_FORMAT_ERROR;
}
}
inStream.Close();
}
else
{
status = STATUS_OPEN_ERROR;
}
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().ToLower();
status = Read( new FileInfo( name ).OpenRead() );
}
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -