package uk.co.mmscomputing.imageio.gif;
import java.io.*;
import java.util.*;
import java.awt.image.*;
import javax.imageio.*;
import javax.imageio.spi.*;
import javax.imageio.stream.*;
import javax.imageio.metadata.*;
public class GIFImageWriter extends ImageWriter{
private GIFFilterOutputStream out;
private int maxWidth=0,maxHeight=0;
private Vector images = null;
int colourCount=0;
int[] colourTable=new int[256];
protected GIFImageWriter(ImageWriterSpi originatingProvider){
super(originatingProvider);
}
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param){
return null;
}
public IIOMetadata convertStreamMetadata(IIOMetadata inData,ImageWriteParam param){
return null;
}
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType,ImageWriteParam param){
return null;
}
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param){
return null;
}
public ImageWriteParam getDefaultWriteParam(){
return new GIFImageWriteParam(getLocale());
}
public boolean canInsertImage(int imageIndex)throws IOException{
super.canInsertImage(imageIndex);
return (imageIndex==0); // use sequence for more than one picture
}
public void write(IIOMetadata streamMetadata,IIOImage img,ImageWriteParam param)throws IOException{
prepareWriteSequence(streamMetadata);
writeToSequence(img,param); // just one page !
endWriteSequence();
}
public boolean canWriteSequence(){
return true;
}
public void prepareWriteSequence(IIOMetadata streamMetadata)throws IOException{
images=new Vector();
colourCount=0;
out=new GIFFilterOutputStream((ImageOutputStream)getOutput());
out.write('G');out.write('I');out.write('F');
out.write('8');out.write('9');out.write('a');
maxWidth=256;maxHeight=256;
out.write(maxWidth&0x000000FF);out.write((maxWidth>>8)&0x000000FF);
out.write(maxHeight&0x000000FF);out.write((maxHeight>>8)&0x000000FF);
int bitColCount=7;
int colres =8;
int flags=bitColCount&0x00000007;
// if(sort){ flags|=0x00000008; }
flags|=((colres-1)&0x00000007)<<4;
// flags|=0x00000080; // has global colour map after header
out.write(flags);
out.write(0); // colourTable[0] is background colour;
out.write(0); // no aspect ratio given
}
public void writeToSequence(IIOImage img,ImageWriteParam param)throws IOException{
if(!(img.getRenderedImage() instanceof BufferedImage)){
throw new IOException(getClass().getName()+".writeToSequence:\n\tCan only write BufferedImage objects");
}
BufferedImage image=(BufferedImage)img.getRenderedImage();
if((image.getType()!=BufferedImage.TYPE_BYTE_INDEXED)||(image.getColorModel().getPixelSize()!=8)){
throw new IOException(getClass().getName()+".writeToSequence:\n\tPlease convert image first to 8 bit 'Byte Indexed' colour model.");
}
int width=image.getWidth();
int height=image.getHeight();
if(width>maxWidth){maxWidth=width;}
if(height>maxHeight){maxHeight=height;}
int bitColCount =7;
int left=0, top=0;
boolean interlaced=false, sort=false, lcm=true;
int flag=(bitColCount&0x00000007);
if(sort){ flag|=0x00000020; }
if(interlaced){ flag|=0x00000040; }
if(lcm){ flag|=0x00000080; } // use local colour table
out.write(','); // 0x2C - Image Descriptor Token
out.write(left &0x000000FF);out.write((left >>8)&0x000000FF);
out.write(top &0x000000FF);out.write((top >>8)&0x000000FF);
out.write(width &0x000000FF);out.write((width >>8)&0x000000FF);
out.write(height&0x000000FF);out.write((height>>8)&0x000000FF);
out.write(flag);
writeColorTable((IndexColorModel)image.getColorModel());
Raster raster = image.getRaster();
DataBufferByte db = (DataBufferByte)raster.getDataBuffer();
byte[] data = db.getData();
GIFLZWOutputStream lzw=new GIFLZWOutputStream(out,bitColCount+1);
lzw.write(data);
lzw.flush();
}
public void endWriteSequence()throws IOException{
out.write(';'); // GIF-Terminator
out.flush();
ImageOutputStream ios=(ImageOutputStream)getOutput();
if(ios instanceof FileCacheImageOutputStream){
((FileCacheImageOutputStream)ios).seek(6);
ios.write(maxWidth&0x000000FF);ios.write((maxWidth>>8)&0x000000FF);
ios.write(maxHeight&0x000000FF);ios.write((maxHeight>>8)&0x000000FF);
}else if(ios instanceof FileImageOutputStream){
((FileImageOutputStream)ios).seek(6);
ios.write(maxWidth&0x000000FF);ios.write((maxWidth>>8)&0x000000FF);
ios.write(maxHeight&0x000000FF);ios.write((maxHeight>>8)&0x000000FF);
}
ios.flush();
// ios.close(); // 2008-09-14 Julien Viet
}
private void writeColorTable(IndexColorModel cm)throws IOException{
int len=cm.getMapSize();
byte[] rColourTable=new byte[len];
byte[] gColourTable=new byte[len];
byte[] bColourTable=new byte[len];
cm.getReds( rColourTable);
cm.getGreens(gColourTable);
cm.getBlues( bColourTable);
for(int i=0; i<len; i++){
out.write(rColourTable[i]);
out.write(gColourTable[i]);
out.write(bColourTable[i]);
}
for(int i=len; i<256; i++){
out.write(0xFF);
out.write(0xFF);
out.write(0xFF);
}
}
static public class GIFFilterOutputStream extends OutputStream{
private ImageOutputStream out;
private byte[] buffer;
private int count,max;
public GIFFilterOutputStream(ImageOutputStream out){
this.out=out;
buffer=new byte[4096];
count=0;
}
public void write(int b)throws IOException{
if(count==buffer.length){
out.write(buffer,0,count);
count=0;
}
buffer[count++]=(byte)b;
}
public void flush()throws IOException{
if(count>0){
out.write(buffer,0,count);
count=0;
}
}
}
}
// http://www.w3.org/Graphics/GIF/spec-gif89a.txt