import java.io.*; import java.awt.*; import java.awt.image.*; public class GIF89aEncoderBlock { short width_, height_; int imagenumber_,numColors_; byte pixels_[], colors_[]; ScreenDescriptor sd_; ImageDescriptor id_; /** * Construct a GIF89aEncoder. The constructor will convert the image to * an indexed color array. This may take some time.
* @param image The image to encode. The image must be * completely loaded. * @exception AWTException Will be thrown if the pixel grab fails. This * can happen if Java runs out of memory. It may also indicate that the image * contains more than 256 colors. * */ public GIF89aEncoderBlock(Image image, int imagenumber) throws AWTException { width_ = (short)image.getWidth(null); height_ = (short)image.getHeight(null); imagenumber_ = imagenumber; int values[] = new int[width_ * height_]; PixelGrabber grabber = new PixelGrabber( image, 0, 0, width_, height_, values, 0, width_); try { if(grabber.grabPixels() != true) throw new AWTException("Grabber returned false: " + grabber.status()); } catch (InterruptedException e) { ; } byte r[][] = new byte[width_][height_]; byte g[][] = new byte[width_][height_]; byte b[][] = new byte[width_][height_]; int index = 0; for (int y = 0; y < height_; ++y) for (int x = 0; x < width_; ++x) { r[x][y] = (byte)((values[index] >> 16) & 0xFF); g[x][y] = (byte)((values[index] >> 8) & 0xFF); b[x][y] = (byte)((values[index]) & 0xFF); ++index; } ToIndexedColor(r, g, b); } /** * Construct a GIFEncoder. The constructor will convert the image to * an indexed color array. This may take some time.
* * Each array stores intensity values for the image. In other words, * r[x][y] refers to the red intensity of the pixel at column x, row * y.
* * @param r An array containing the red intensity values. * @param g An array containing the green intensity values. * @param b An array containing the blue intensity values. * * @exception AWTException Will be thrown if the image contains more than * 256 colors. * */ public GIF89aEncoderBlock(byte r[][], byte g[][], byte b[][], int imagenumber) throws AWTException { width_ = (short)(r.length); height_ = (short)(r[0].length); imagenumber_ = imagenumber; ToIndexedColor(r, g, b); } /** * Writes the image out to a stream in the GIF file format. This will * be a single GIF89a image, non-interlaced, with no background color. * This may take some time.
*
* @param output The stream to output to. This should probably be a
* buffered stream.
*
* @exception IOException Will be thrown if a write operation fails.
* */
public void Write(OutputStream output) throws IOException {
if (imagenumber_ == 1) // First image uses global color map
{
BitUtils.WriteString(output, "GIF89a"); //Write header
ScreenDescriptor sd = new ScreenDescriptor(width_, height_,numColors_);
sd.Write(output);
//Write global color map
output.write(colors_, 0, colors_.length);
//Write Netscape Extension Block with 1,000 loops
short nloop = 1000;
NetscapeAppExt ns = new NetscapeAppExt(nloop);
ns.Write(output);
//Write Graphic Control Extension Block
GraphicConExt gc = new GraphicConExt();
gc.Write(output);
//Write ImageDescriptor Block
ImageDescriptor id = new ImageDescriptor(width_, height_, ',');
id.SetLocalColorTableSize((byte) (BitUtils.BitsNeeded(numColors_) -1));////////TESTESTESTEST
id.Write(output);
//Write Code size to image data block
byte codesize = BitUtils.BitsNeeded(numColors_);
if (codesize == 1)
++codesize;
output.write(codesize);
//Write image data block
LZWCompressor.LZWCompress(output, codesize, pixels_);
output.write(0);
}
else { //Second image and so on use local color map
//Write Graphic Control Extension Block
GraphicConExt gc = new GraphicConExt();
gc.Write(output);
//Write ImageDescriptor Block
ImageDescriptor id = new ImageDescriptor(width_, height_, ',');
id.SetLocalColorTableSize((byte) (BitUtils.BitsNeeded(numColors_) -1)); //local color map size
id.SetLocalColorTableFlag((byte) 1); //M =1 because of using Local color map
id.Write(output);
//Write local color map
output.write(colors_, 0, colors_.length); //If M =0, we have to skip this line
//Write Code size to image data block
byte codesize = BitUtils.BitsNeeded(numColors_);
if (codesize == 1)
++codesize;
output.write(codesize);
//Write image data block
LZWCompressor.LZWCompress(output, codesize, pixels_);
output.write(0);
} //End else
}
public static void WriteCommentEnd(OutputStream output,String comment) throws IOException {
//Add comment to the file
CommentExt cx = new CommentExt(comment);
cx.Write(output);
ImageDescriptor id = new ImageDescriptor((byte)0, (byte)0, ';'); //Write Gif Trialer
id.Write(output);
output.flush();
}
void ToIndexedColor(byte r[][], byte g[][],
byte b[][]) throws AWTException {
pixels_ = new byte[width_ * height_];
colors_ = new byte[256 * 3];
int colornum = 0;
for (int x = 0; x < width_; ++x) {
for (int y = 0; y < height_; ++y) {
int search;
for (search = 0; search < colornum; ++search)
if (colors_[search * 3] == r[x][y] &&
colors_[search * 3 + 1] == g[x][y] &&
colors_[search * 3 + 2] == b[x][y])
break;
if (search > 255) throw new AWTException("Too many colors.");
pixels_[y * width_ + x] = (byte)search;
if (search == colornum) {
colors_[search * 3] = r[x][y];
colors_[search * 3 + 1] = g[x][y];
colors_[search * 3 + 2] = b[x][y];
++colornum;
}
}
}
numColors_ = 1 << BitUtils.BitsNeeded(colornum);
byte copy[] = new byte[numColors_ * 3];
System.arraycopy(colors_, 0, copy, 0, numColors_ * 3);
colors_ = copy;
}
}
class BitFile {
OutputStream output_;
byte buffer_[];
int index_, bitsLeft_;
public BitFile(OutputStream output) {
output_ = output;
buffer_ = new byte[256];
index_ = 0;
bitsLeft_ = 8;
}
public void Flush() throws IOException {
int numBytes = index_ + (bitsLeft_ == 8 ? 0 : 1);
if (numBytes > 0) {
output_.write(numBytes);
output_.write(buffer_, 0, numBytes);
buffer_[0] = 0;
index_ = 0;
bitsLeft_ = 8;
}
}
public void WriteBits(int bits, int numbits) throws IOException {
int bitsWritten = 0;
int numBytes = 255;
do {
if ((index_ == 254 && bitsLeft_ == 0) || index_ > 254) {
output_.write(numBytes);
output_.write(buffer_, 0, numBytes);
buffer_[0] = 0;
index_ = 0;
bitsLeft_ = 8;
}
if (numbits <= bitsLeft_) {
buffer_[index_] |= (bits & ((1 << numbits) - 1)) <<
(8 - bitsLeft_);
bitsWritten += numbits;
bitsLeft_ -= numbits;
numbits = 0;
}
else {
buffer_[index_] |= (bits & ((1 << bitsLeft_) - 1)) <<
(8 - bitsLeft_);
bitsWritten += bitsLeft_;
bits >>= bitsLeft_;
numbits -= bitsLeft_;
buffer_[++index_] = 0;
bitsLeft_ = 8;
}
} while (numbits != 0);
}
}
class LZWStringTable {
private final static int RES_CODES = 2;
private final static short HASH_FREE = (short)0xFFFF;
private final static short NEXT_FIRST = (short)0xFFFF;
private final static int MAXBITS = 12;
private final static int MAXSTR = (1 << MAXBITS);
private final static short HASHSIZE = 9973;
private final static short HASHSTEP = 2039;
byte strChr_[];
short strNxt_[];
short strHsh_[];
short numStrings_;
public LZWStringTable() {
strChr_ = new byte[MAXSTR];
strNxt_ = new short[MAXSTR];
strHsh_ = new short[HASHSIZE];
}
public int AddCharString(short index, byte b) {
int hshidx;
if (numStrings_ >= MAXSTR)
return 0xFFFF;
hshidx = Hash(index, b);
while (strHsh_[hshidx] != HASH_FREE)
hshidx = (hshidx + HASHSTEP) % HASHSIZE;
strHsh_[hshidx] = numStrings_;
strChr_[numStrings_] = b;
strNxt_[numStrings_] = (index != HASH_FREE) ? index : NEXT_FIRST;
return numStrings_++;
}
public short FindCharString(short index, byte b) {
int hshidx, nxtidx;
if (index == HASH_FREE)
return b;
hshidx = Hash(index, b);
while ((nxtidx = strHsh_[hshidx]) != HASH_FREE) {
if (strNxt_[nxtidx] == index && strChr_[nxtidx] == b)
return (short)nxtidx;
hshidx = (hshidx + HASHSTEP) % HASHSIZE;
}
return (short)0xFFFF;
}
public void ClearTable(int codesize) {
numStrings_ = 0;
for (int q = 0; q < HASHSIZE; q++) {
strHsh_[q] = HASH_FREE;
}
int w = (1 << codesize) + RES_CODES;
for (int q = 0; q < w; q++)
AddCharString((short)0xFFFF, (byte)q);
}
static public int Hash(short index, byte lastbyte) {
return ((int)((short)(lastbyte << 8) ^ index) & 0xFFFF) % HASHSIZE;
}
}
class LZWCompressor {
public static void LZWCompress(OutputStream output, int codesize,
byte toCompress[]) throws IOException {
byte c;
short index;
int clearcode, endofinfo, numbits, limit, errcode;
short prefix = (short)0xFFFF;
BitFile bitFile = new BitFile(output);
LZWStringTable strings = new LZWStringTable();
clearcode = 1 << codesize;
endofinfo = clearcode + 1;
numbits = codesize + 1;
limit = (1 << numbits) - 1;
strings.ClearTable(codesize);
bitFile.WriteBits(clearcode, numbits);
for (int loop = 0; loop < toCompress.length; ++loop) {
c = toCompress[loop];
if ((index = strings.FindCharString(prefix, c)) != -1)
prefix = index;
else {
bitFile.WriteBits(prefix, numbits);
if (strings.AddCharString(prefix, c) > limit) {
if (++numbits > 12) {
bitFile.WriteBits(clearcode, numbits - 1);
strings.ClearTable(codesize);
numbits = codesize + 1;
}
limit = (1 << numbits) - 1;
}
prefix = (short)((short)c & 0xFF);
}
}
if (prefix != -1)
bitFile.WriteBits(prefix, numbits);
bitFile.WriteBits(endofinfo, numbits);
bitFile.Flush();
}
}
class ScreenDescriptor {
public short localScreenWidth_, localScreenHeight_;
private byte byte_;
public byte backgroundColorIndex_, pixelAspectRatio_;
public ScreenDescriptor(short width, short height, int numColors) {
localScreenWidth_ = width;
localScreenHeight_ = height;
SetGlobalColorTableSize((byte)(BitUtils.BitsNeeded(numColors) - 1));
SetGlobalColorTableFlag((byte)1);
SetSortFlag((byte)0);
SetColorResolution((byte)7);
backgroundColorIndex_ = 0;
pixelAspectRatio_ = 0;
}
public void Write(OutputStream output) throws IOException {
BitUtils.WriteWord(output, localScreenWidth_);
BitUtils.WriteWord(output, localScreenHeight_);
output.write(byte_);
output.write(backgroundColorIndex_);
output.write(pixelAspectRatio_);
}
public void SetGlobalColorTableSize(byte num) {
byte_ |= (num & 7);
}
public void SetSortFlag(byte num) {
byte_ |= (num & 1) << 3;
}
public void SetColorResolution(byte num) {
byte_ |= (num & 7) << 4;
}
public void SetGlobalColorTableFlag(byte num) {
byte_ |= (num & 1) << 7;
}
}
class ImageDescriptor {
public byte separator_;
public short leftPosition_, topPosition_, width_, height_;
private byte byte_;
public ImageDescriptor(short width, short height, char separator) {
separator_ = (byte)separator;
leftPosition_ = 0;
topPosition_ = 0;
width_ = width;
height_ = height;
SetLocalColorTableSize((byte)0);
SetReserved((byte)0);
SetSortFlag((byte)0);
SetInterlaceFlag((byte)0);
SetLocalColorTableFlag((byte)0);
}
public void Write(OutputStream output) throws IOException {
output.write(separator_);
BitUtils.WriteWord(output, leftPosition_);
BitUtils.WriteWord(output, topPosition_);
BitUtils.WriteWord(output, width_);
BitUtils.WriteWord(output, height_);
output.write(byte_);
}
public void SetLocalColorTableSize(byte num) {
byte_ |= (num & 7);
}
public void SetReserved(byte num) {
byte_ |= (num & 3) << 3;
}
public void SetSortFlag(byte num) {
byte_ |= (num & 1) << 5;
}
public void SetInterlaceFlag(byte num) {
byte_ |= (num & 1) << 6;
}
public void SetLocalColorTableFlag(byte num) {
byte_ |= (num & 1) << 7;
}
}
//Netscape Application Extension Block
class NetscapeAppExt {
private final static byte gifExtCode_ = 33;
private final static byte appExtLabel_ = -1;
private final static byte lengthAppBlock_ = 11;
private final static byte lengthDataSubB_ = 3;
private final static byte byte16_ = 1;
public short loop_;
private final static byte dataSubBTer_ = 0;
public NetscapeAppExt(short nloop) {
loop_ = nloop;
}
public void Write(OutputStream output) throws IOException {
output.write(gifExtCode_);
output.write(appExtLabel_);
output.write(lengthAppBlock_);
BitUtils.WriteString(output, "NETSCAPE");
BitUtils.WriteString(output, "2.0");
output.write(lengthDataSubB_);
output.write(byte16_);
BitUtils.WriteWord(output, loop_);
output.write(dataSubBTer_);
}
}
//Comment Extension Block
class CommentExt {
private final static byte gifExtCode_ = 33;
private final static byte commentLabel_ = -2;
public String comment_;
private byte blocksize_;
private final static byte dataSubBTer_ = 0;
public CommentExt(String comment) {
comment_ = new String(comment);
blocksize_ = (byte) comment_.length();
}
public void Write(OutputStream output) throws IOException {
output.write(gifExtCode_);
output.write(commentLabel_);
output.write(blocksize_);
BitUtils.WriteString(output, comment_);
output.write(dataSubBTer_);
}
}
//Graphic Control Extension Block
class GraphicConExt {
private final static byte gifExtCode_ = 33;
private final static byte graphicConLabel_ = -7;
private final static byte blockSize_ = 4;
public byte byte_;
public short delay_;
public byte tColorIndex_;
private final static byte dataSubBTer_ = 0;
public GraphicConExt() {
byte_ = 0;
SetTransparentColorFlag((byte)0); //No Transparent Color
SetUserInputFlag((byte) 0); //No User Input required
SetDisposalMethodValue((byte) 0); //Nothing Mode = 0
SetReserved((byte)0); //Reserved = 000
// SetDelayTime((short)100); //Default Delay Time = 1/100 second
SetDelayTime((short)10); //Special for Gifin ^_^
SetTransparentColorIndex((short) 0);//Default Transparent Color = 0
}
public void Write(OutputStream output) throws IOException {
output.write(gifExtCode_); //Extension Introducer
output.write(graphicConLabel_); //Graphic Control Lable
output.write(blockSize_); //Block Size
output.write(byte_); //