packagecom.yeetrack.selenium.Image;import java.io.*;import java.awt.*;import java.awt.image.*;/*** Class AnimatedGifEncoder - Encodes a GIF file consisting of one or
* more frames.
*
* Example:
* AnimatedGifEncoder e = new AnimatedGifEncoder();
* e.start(outputFileName);
* e.setDelay(1000); // 1 frame per sec
* e.addFrame(image1);
* e.addFrame(image2);
* e.finish();
*
* No copyright asserted on the source code of this class. May be used
* for any purpose, however, refer to the Unisys LZW patent for restrictions
* on use of the associated LZWEncoder class. Please forward any corrections
* to kweiner@fmsware.com.
*
*@authorKevin Weiner, FM Software
*@version1.03 November 2003
**/
public classAnimatedGifEncoder {protected int width; //image size
protected intheight;protected Color transparent = null; //transparent color if given
protected int transIndex; //transparent index in color table
protected int repeat = -1; //no repeat
protected int delay = 0; //frame delay (hundredths)
protected boolean started = false; //ready to output frames
protectedOutputStream out;protected BufferedImage image; //current frame
protected byte[] pixels; //BGR byte array from frame
protected byte[] indexedPixels; //converted frame indexed to palette
protected int colorDepth; //number of bit planes
protected byte[] colorTab; //RGB palette
protected boolean[] usedEntry = new boolean[256]; //active palette entries
protected int palSize = 7; //color table size (bits-1)
protected int dispose = -1; //disposal code (-1 = use default)
protected boolean closeStream = false; //close stream when finished
protected boolean firstFrame = true;protected boolean sizeSet = false; //if false, get size from first frame
protected int sample = 10; //default sample interval for quantizer
/*** Sets the delay time between each frame, or changes it
* for subsequent frames (applies to last frame added).
*
*@paramms int delay time in milliseconds*/
public void setDelay(intms) {
delay= Math.round(ms / 10.0f);
}/*** Sets the GIF frame disposal code for the last added frame
* and any subsequent frames. Default is 0 if no transparent
* color has been set, otherwise 2.
*@paramcode int disposal code.*/
public void setDispose(intcode) {if (code >= 0) {
dispose=code;
}
}/*** Sets the number of times the set of GIF frames
* should be played. Default is 1; 0 means play
* indefinitely. Must be invoked before the first
* image is added.
*
*@paramiter int number of iterations.
*@return
*/
public void setRepeat(intiter) {if (iter >= 0) {
repeat=iter;
}
}/*** Sets the transparent color for the last added frame
* and any subsequent frames.
* Since all colors are subject to modification
* in the quantization process, the color in the final
* palette for each frame closest to the given color
* becomes the transparent color for that frame.
* May be set to null to indicate no transparent color.
*
*@paramc Color to be treated as transparent on display.*/
public voidsetTransparent(Color c) {
transparent=c;
}/*** Adds next GIF frame. The frame is not written immediately, but is
* actually deferred until the next frame is received so that timing
* data can be inserted. Invoking finish()
flushes all
* frames. If setSize
was not invoked, the size of the
* first image is used for all subsequent frames.
*
*@paramim BufferedImage containing frame to write.
*@returntrue if successful.*/
public booleanaddFrame(BufferedImage im) {if ((im == null) || !started) {return false;
}boolean ok = true;try{if (!sizeSet) {//use first frame's size
setSize(im.getWidth(), im.getHeight());
}
image=im;
getImagePixels();//convert to correct format if necessary
analyzePixels(); //build color table & map pixels
if(firstFrame) {
writeLSD();//logical screen descriptior
writePalette(); //global color table
if (repeat >= 0) {//use NS app extension to indicate reps
writeNetscapeExt();
}
}
writeGraphicCtrlExt();//write graphic control extension
writeImageDesc(); //image descriptor
if (!firstFrame) {
writePalette();//local color table
}
writePixels();//encode and write pixel data
firstFrame = false;
}catch(IOException e) {
ok= false;
}returnok;
}/*** Flushes any pending data and closes output file.
* If writing to an OutputStream, the stream is not
* closed.*/
public booleanfinish() {if (!started) return false;boolean ok = true;
started= false;try{
out.write(0x3b); //gif trailer
out.flush();if(closeStream) {
out.close();
}
}catch(IOException e) {
ok= false;
}//reset for subsequent use
transIndex = 0;
out= null;
image= null;
pixels= null;
indexedPixels= null;
colorTab= null;
closeStream= false;
firstFrame= true;returnok;
}/*** Sets frame rate in frames per second. Equivalent to
* setDelay(1000/fps)
.
*
*@paramfps float frame rate (frames per second)*/
public void setFrameRate(floatfps) {if (fps !=0f) {
delay= Math.round(100f /fps);
}
}/*** Sets quality of color quantization (conversion of images
* to the maximum 256 colors allowed by the GIF specification).
* Lower values (minimum = 1) produce better colors, but slow
* processing significantly. 10 is the default, and produces
* good color mapping at reasonable speeds. Values greater
* than 20 do not yield significant improvements in speed.
*
*@paramquality int greater than 0.
*@return
*/
public void setQuality(intquality) {if (quality < 1) quality = 1;
sample=quality;
}/*** Sets the GIF frame size. The default size is the
* size of the first frame added if this method is
* not invoked.
*
*@paramw int frame width.
*@paramh int frame width.*/
public void setSize(int w, inth) {if (started && !firstFrame) return;
width=w;
height=h;if (width < 1) width = 320;if (height < 1) height = 240;
sizeSet= true;
}/*** Initiates GIF file creation on the given stream. The stream
* is not closed automatically.
*
*@paramos OutputStream on which GIF images are written.
*@returnfalse if initial write failed.*/
public booleanstart(OutputStream os) {if (os == null) return false;boolean ok = true;
closeStream= false;
out=os;try{
writeString("GIF89a"); //header
} catch(IOException e) {
ok= false;
}return started =ok;
}/*** Initiates writing of a GIF file with the specified name.
*
*@paramfile String containing output file name.
*@returnfalse if open or initial write failed.*/
public booleanstart(String file) {boolean ok = true;try{
out= new BufferedOutputStream(newFileOutputStream(file));
ok=start(out);
closeStream= true;
}catch(IOException e) {
ok= false;
}return started =ok;
}/*** Analyzes image colors and creates color map.*/
protected voidanalyzePixels() {int len =pixels.length;int nPix = len / 3;
indexedPixels= new byte[nPix];
NeuQuant nq= newNeuQuant(pixels, len, sample);//initialize quantizer
colorTab = nq.process(); //create reduced palette//convert map from BGR to RGB
for (int i = 0; i < colorTab.length; i += 3) {byte temp =colorTab[i];
colorTab[i]= colorTab[i + 2];
colorTab[i+ 2] =temp;
usedEntry[i/ 3] = false;
}//map image pixels to new palette
int k = 0;for (int i = 0; i < nPix; i++) {int index =nq.map(pixels[k++] & 0xff,
pixels[k++] & 0xff,
pixels[k++] & 0xff);
usedEntry[index]= true;
indexedPixels[i]= (byte) index;
}
pixels= null;
colorDepth= 8;
palSize= 7;//get closest match to transparent color if specified
if (transparent != null) {
transIndex=findClosest(transparent);
}
}/*** Returns index of palette color closest to c
**/
protected intfindClosest(Color c) {if (colorTab == null) return -1;int r =c.getRed();int g =c.getGreen();int b =c.getBlue();int minpos = 0;int dmin = 256 * 256 * 256;int len =colorTab.length;for (int i = 0; i
dmin=d;
minpos=index;
}
i++;
}returnminpos;
}/*** Extracts image pixels into byte array "pixels"*/
protected voidgetImagePixels() {int w =image.getWidth();int h =image.getHeight();int type =image.getType();if ((w !=width)|| (h !=height)|| (type !=BufferedImage.TYPE_3BYTE_BGR)) {//create new image with right size/format
BufferedImage temp =
newBufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
Graphics2D g=temp.createGraphics();
g.drawImage(image,0, 0, null);
image=temp;
}
pixels=((DataBufferByte) image.getRaster().getDataBuffer()).getData();
}/*** Writes Graphic Control Extension*/
protected void writeGraphicCtrlExt() throwsIOException {
out.write(0x21); //extension introducer
out.write(0xf9); //GCE label
out.write(4); //data block size
inttransp, disp;if (transparent == null) {
transp= 0;
disp= 0; //dispose = no action
} else{
transp= 1;
disp= 2; //force clear if using transparent color
}if (dispose >= 0) {
disp= dispose & 7; //user override
}
disp<<= 2;//packed fields
out.write(0 | //1:3 reserved
disp | //4:6 disposal
0 | //7 user input - 0 = none
transp); //8 transparency flag
writeShort(delay); //delay x 1/100 sec
out.write(transIndex); //transparent color index
out.write(0); //block terminator
}/*** Writes Image Descriptor*/
protected void writeImageDesc() throwsIOException {
out.write(0x2c); //image separator
writeShort(0); //image position x,y = 0,0
writeShort(0);
writeShort(width);//image size
writeShort(height);//packed fields
if(firstFrame) {//no LCT - GCT is used for first (or only) frame
out.write(0);
}else{//specify normal LCT
out.write(0x80 | //1 local color table 1=yes
0 | //2 interlace - 0=no
0 | //3 sorted - 0=no
0 | //4-5 reserved
palSize); //6-8 size of color table
}
}/*** Writes Logical Screen Descriptor*/
protected void writeLSD() throwsIOException {//logical screen size
writeShort(width);
writeShort(height);//packed fields
out.write((0x80 | //1 : global color table flag = 1 (gct used)
0x70 | //2-4 : color resolution = 7
0x00 | //5 : gct sort flag = 0
palSize)); //6-8 : gct size
out.write(0); //background color index
out.write(0); //pixel aspect ratio - assume 1:1
}/*** Writes Netscape application extension to define
* repeat count.*/
protected void writeNetscapeExt() throwsIOException {
out.write(0x21); //extension introducer
out.write(0xff); //app extension label
out.write(11); //block size
writeString("NETSCAPE" + "2.0"); //app id + auth code
out.write(3); //sub-block size
out.write(1); //loop sub-block id
writeShort(repeat); //loop count (extra iterations, 0=repeat forever)
out.write(0); //block terminator
}/*** Writes color table*/
protected void writePalette() throwsIOException {
out.write(colorTab,0, colorTab.length);int n = (3 * 256) -colorTab.length;for (int i = 0; i < n; i++) {
out.write(0);
}
}/*** Encodes and writes pixel data*/
protected void writePixels() throwsIOException {
LZWEncoder encoder=
newLZWEncoder(width, height, indexedPixels, colorDepth);
encoder.encode(out);
}/*** Write 16-bit value to output stream, LSB first*/
protected void writeShort(int value) throwsIOException {
out.write(value& 0xff);
out.write((value>> 8) & 0xff);
}/*** Writes string to output stream*/
protected void writeString(String s) throwsIOException {for (int i = 0; i < s.length(); i++) {
out.write((byte) s.charAt(i));
}
}
}