From: <sa...@us...> - 2003-10-28 14:54:11
|
Update of /cvsroot/jrobin/src/jrobin/graph In directory sc8-pr-cvs1:/tmp/cvs-serv28252/jrobin/graph Modified Files: Grapher.java RrdGraph.java Added Files: GifEncoder.java Log Message: GIF Encoder --- NEW FILE: GifEncoder.java --- package jrobin.graph; import java.awt.*; import java.awt.image.PixelGrabber; import java.io.*; import java.util.Vector; // thanks to J. M. G. Elliott // http://jmge.net/java/gifenc/ class Gif89Encoder { private Dimension dispDim = new Dimension(0, 0); private GifColorTable colorTable; private int bgIndex = 0; private int loopCount = 1; private String theComments; private Vector vFrames = new Vector(); Gif89Encoder() { colorTable = new GifColorTable(); } Gif89Encoder(Image static_image) throws IOException { this(); addFrame(static_image); } Gif89Encoder(Color[] colors) { colorTable = new GifColorTable(colors); } Gif89Encoder(Color[] colors, int width, int height, byte ci_pixels[]) throws IOException { this(colors); addFrame(width, height, ci_pixels); } int getFrameCount() { return vFrames.size(); } Gif89Frame getFrameAt(int index) { return isOk(index) ? (Gif89Frame) vFrames.elementAt(index) : null; } void addFrame(Gif89Frame gf) throws IOException { accommodateFrame(gf); vFrames.addElement(gf); } void addFrame(Image image) throws IOException { addFrame(new DirectGif89Frame(image)); } void addFrame(int width, int height, byte ci_pixels[]) throws IOException { addFrame(new IndexGif89Frame(width, height, ci_pixels)); } void insertFrame(int index, Gif89Frame gf) throws IOException { accommodateFrame(gf); vFrames.insertElementAt(gf, index); } void setTransparentIndex(int index) { colorTable.setTransparent(index); } void setLogicalDisplay(Dimension dim, int background) { dispDim = new Dimension(dim); bgIndex = background; } void setLoopCount(int count) { loopCount = count; } void setComments(String comments) { theComments = comments; } void setUniformDelay(int interval) { for (int i = 0; i < vFrames.size(); ++i) ((Gif89Frame) vFrames.elementAt(i)).setDelay(interval); } void encode(OutputStream out) throws IOException { int nframes = getFrameCount(); boolean is_sequence = nframes > 1; colorTable.closePixelProcessing(); Put.ascii("GIF89a", out); writeLogicalScreenDescriptor(out); colorTable.encode(out); if (is_sequence && loopCount != 1) writeNetscapeExtension(out); if (theComments != null && theComments.length() > 0) writeCommentExtension(out); for (int i = 0; i < nframes; ++i) ((Gif89Frame) vFrames.elementAt(i)).encode( out, is_sequence, colorTable.getDepth(), colorTable.getTransparent() ); out.write((int) ';'); out.flush(); } private void accommodateFrame(Gif89Frame gf) throws IOException { dispDim.width = Math.max(dispDim.width, gf.getWidth()); dispDim.height = Math.max(dispDim.height, gf.getHeight()); colorTable.processPixels(gf); } private void writeLogicalScreenDescriptor(OutputStream os) throws IOException { Put.leShort(dispDim.width, os); Put.leShort(dispDim.height, os); os.write(0xf0 | colorTable.getDepth() - 1); os.write(bgIndex); os.write(0); } private void writeNetscapeExtension(OutputStream os) throws IOException { os.write((int) '!'); os.write(0xff); os.write(11); Put.ascii("NETSCAPE2.0", os); os.write(3); os.write(1); Put.leShort(loopCount > 1 ? loopCount - 1 : 0, os); os.write(0); } private void writeCommentExtension(OutputStream os) throws IOException { os.write((int) '!'); os.write(0xfe); int remainder = theComments.length() % 255; int nsubblocks_full = theComments.length() / 255; int nsubblocks = nsubblocks_full + (remainder > 0 ? 1 : 0); int ibyte = 0; for (int isb = 0; isb < nsubblocks; ++isb) { int size = isb < nsubblocks_full ? 255 : remainder; os.write(size); Put.ascii(theComments.substring(ibyte, ibyte + size), os); ibyte += size; } os.write(0); } private boolean isOk(int frame_index) { return frame_index >= 0 && frame_index < vFrames.size(); } } class DirectGif89Frame extends Gif89Frame { private int[] argbPixels; DirectGif89Frame(Image img) throws IOException { PixelGrabber pg = new PixelGrabber(img, 0, 0, -1, -1, true); String errmsg = null; try { if (!pg.grabPixels()) errmsg = "can't grab pixels from image"; } catch (InterruptedException e) { errmsg = "interrupted grabbing pixels from image"; } if (errmsg != null) throw new IOException(errmsg + " (" + getClass().getName() + ")"); theWidth = pg.getWidth(); theHeight = pg.getHeight(); argbPixels = (int[]) pg.getPixels(); ciPixels = new byte[argbPixels.length]; } DirectGif89Frame(int width, int height, int argb_pixels[]) { theWidth = width; theHeight = height; argbPixels = new int[theWidth * theHeight]; System.arraycopy(argb_pixels, 0, argbPixels, 0, argbPixels.length); ciPixels = new byte[argbPixels.length]; } Object getPixelSource() { return argbPixels; } } class GifColorTable { private int[] theColors = new int[256]; private int colorDepth; private int transparentIndex = -1; private int ciCount = 0; private ReverseColorMap ciLookup; GifColorTable() { ciLookup = new ReverseColorMap(); } GifColorTable(Color[] colors) { int n2copy = Math.min(theColors.length, colors.length); for (int i = 0; i < n2copy; ++i) theColors[i] = colors[i].getRGB(); } int getDepth() { return colorDepth; } int getTransparent() { return transparentIndex; } void setTransparent(int color_index) { transparentIndex = color_index; } void processPixels(Gif89Frame gf) throws IOException { if (gf instanceof DirectGif89Frame) filterPixels((DirectGif89Frame) gf); else trackPixelUsage((IndexGif89Frame) gf); } void closePixelProcessing() { colorDepth = computeColorDepth(ciCount); } void encode(OutputStream os) throws IOException { int palette_size = 1 << colorDepth; for (int i = 0; i < palette_size; ++i) { os.write(theColors[i] >> 16 & 0xff); os.write(theColors[i] >> 8 & 0xff); os.write(theColors[i] & 0xff); } } private void filterPixels(DirectGif89Frame dgf) throws IOException { if (ciLookup == null) throw new IOException("RGB frames require palette autodetection"); int[] argb_pixels = (int[]) dgf.getPixelSource(); byte[] ci_pixels = dgf.getPixelSink(); int npixels = argb_pixels.length; for (int i = 0; i < npixels; ++i) { int argb = argb_pixels[i]; if ((argb >>> 24) < 0x80) if (transparentIndex == -1) transparentIndex = ciCount; else if (argb != theColors[transparentIndex]) { ci_pixels[i] = (byte) transparentIndex; continue; } int color_index = ciLookup.getPaletteIndex(argb & 0xffffff); if (color_index == -1) { if (ciCount == 256) throw new IOException("can't encode as GIF (> 256 colors)"); theColors[ciCount] = argb; ciLookup.put(argb & 0xffffff, ciCount); ci_pixels[i] = (byte) ciCount; ++ciCount; } else ci_pixels[i] = (byte) color_index; } } private void trackPixelUsage(IndexGif89Frame igf) { byte[] ci_pixels = (byte[]) igf.getPixelSource(); int npixels = ci_pixels.length; for (int i = 0; i < npixels; ++i) if (ci_pixels[i] >= ciCount) ciCount = ci_pixels[i] + 1; } private int computeColorDepth(int colorcount) { if (colorcount <= 2) return 1; if (colorcount <= 4) return 2; if (colorcount <= 16) return 4; return 8; } } class ReverseColorMap { private static class ColorRecord { int rgb; int ipalette; ColorRecord(int rgb, int ipalette) { this.rgb = rgb; this.ipalette = ipalette; } } private static final int HCAPACITY = 2053; private ColorRecord[] hTable = new ColorRecord[HCAPACITY]; int getPaletteIndex(int rgb) { ColorRecord rec; for (int itable = rgb % hTable.length; (rec = hTable[itable]) != null && rec.rgb != rgb; itable = ++itable % hTable.length ) ; if (rec != null) return rec.ipalette; return -1; } void put(int rgb, int ipalette) { int itable; for (itable = rgb % hTable.length; hTable[itable] != null; itable = ++itable % hTable.length ) ; hTable[itable] = new ColorRecord(rgb, ipalette); } } abstract class Gif89Frame { static final int DM_UNDEFINED = 0; static final int DM_LEAVE = 1; static final int DM_BGCOLOR = 2; static final int DM_REVERT = 3; int theWidth = -1; int theHeight = -1; byte[] ciPixels; private Point thePosition = new Point(0, 0); private boolean isInterlaced; private int csecsDelay; private int disposalCode = DM_LEAVE; void setPosition(Point p) { thePosition = new Point(p); } void setInterlaced(boolean b) { isInterlaced = b; } void setDelay(int interval) { csecsDelay = interval; } void setDisposalMode(int code) { disposalCode = code; } Gif89Frame() { } abstract Object getPixelSource(); int getWidth() { return theWidth; } int getHeight() { return theHeight; } byte[] getPixelSink() { return ciPixels; } void encode(OutputStream os, boolean epluribus, int color_depth, int transparent_index) throws IOException { writeGraphicControlExtension(os, epluribus, transparent_index); writeImageDescriptor(os); new GifPixelsEncoder( theWidth, theHeight, ciPixels, isInterlaced, color_depth ).encode(os); } private void writeGraphicControlExtension(OutputStream os, boolean epluribus, int itransparent) throws IOException { int transflag = itransparent == -1 ? 0 : 1; if (transflag == 1 || epluribus) { os.write((int) '!'); os.write(0xf9); os.write(4); os.write((disposalCode << 2) | transflag); Put.leShort(csecsDelay, os); os.write(itransparent); os.write(0); } } private void writeImageDescriptor(OutputStream os) throws IOException { os.write((int) ','); Put.leShort(thePosition.x, os); Put.leShort(thePosition.y, os); Put.leShort(theWidth, os); Put.leShort(theHeight, os); os.write(isInterlaced ? 0x40 : 0); } } class GifPixelsEncoder { private static final int EOF = -1; private int imgW, imgH; private byte[] pixAry; private boolean wantInterlaced; private int initCodeSize; private int countDown; private int xCur, yCur; private int curPass; GifPixelsEncoder(int width, int height, byte[] pixels, boolean interlaced, int color_depth) { imgW = width; imgH = height; pixAry = pixels; wantInterlaced = interlaced; initCodeSize = Math.max(2, color_depth); } void encode(OutputStream os) throws IOException { os.write(initCodeSize); countDown = imgW * imgH; xCur = yCur = curPass = 0; compress(initCodeSize + 1, os); os.write(0); } private void bumpPosition() { ++xCur; if (xCur == imgW) { xCur = 0; if (!wantInterlaced) ++yCur; else switch (curPass) { case 0: yCur += 8; if (yCur >= imgH) { ++curPass; yCur = 4; } break; case 1: yCur += 8; if (yCur >= imgH) { ++curPass; yCur = 2; } break; case 2: yCur += 4; if (yCur >= imgH) { ++curPass; yCur = 1; } break; case 3: yCur += 2; break; } } } private int nextPixel() { if (countDown == 0) return EOF; --countDown; byte pix = pixAry[yCur * imgW + xCur]; bumpPosition(); return pix & 0xff; } static final int BITS = 12; static final int HSIZE = 5003; int n_bits; int maxbits = BITS; int maxcode; int maxmaxcode = 1 << BITS; final int MAXCODE(int n_bits) { return (1 << n_bits) - 1; } int[] htab = new int[HSIZE]; int[] codetab = new int[HSIZE]; int hsize = HSIZE; int free_ent = 0; boolean clear_flg = false; int g_init_bits; int ClearCode; int EOFCode; void compress(int init_bits, OutputStream outs) throws IOException { int fcode; int i /* = 0 */; int c; int ent; int disp; int hsize_reg; int hshift; g_init_bits = init_bits; clear_flg = false; n_bits = g_init_bits; maxcode = MAXCODE(n_bits); ClearCode = 1 << (init_bits - 1); EOFCode = ClearCode + 1; free_ent = ClearCode + 2; char_init(); ent = nextPixel(); hshift = 0; for (fcode = hsize; fcode < 65536; fcode *= 2) ++hshift; hshift = 8 - hshift; hsize_reg = hsize; cl_hash(hsize_reg); output(ClearCode, outs); outer_loop: while ((c = nextPixel()) != EOF) { fcode = (c << maxbits) + ent; i = (c << hshift) ^ ent; if (htab[i] == fcode) { ent = codetab[i]; continue; } else if (htab[i] >= 0) { disp = hsize_reg - i; if (i == 0) disp = 1; do { if ((i -= disp) < 0) i += hsize_reg; if (htab[i] == fcode) { ent = codetab[i]; continue outer_loop; } } while (htab[i] >= 0); } output(ent, outs); ent = c; if (free_ent < maxmaxcode) { codetab[i] = free_ent++; htab[i] = fcode; } else cl_block(outs); } output(ent, outs); output(EOFCode, outs); } int cur_accum = 0; int cur_bits = 0; int masks[] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; void output(int code, OutputStream outs) throws IOException { cur_accum &= masks[cur_bits]; if (cur_bits > 0) cur_accum |= (code << cur_bits); else cur_accum = code; cur_bits += n_bits; while (cur_bits >= 8) { char_out((byte) (cur_accum & 0xff), outs); cur_accum >>= 8; cur_bits -= 8; } if (free_ent > maxcode || clear_flg) { if (clear_flg) { maxcode = MAXCODE(n_bits = g_init_bits); clear_flg = false; } else { ++n_bits; if (n_bits == maxbits) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } if (code == EOFCode) { while (cur_bits > 0) { char_out((byte) (cur_accum & 0xff), outs); cur_accum >>= 8; cur_bits -= 8; } flush_char(outs); } } void cl_block(OutputStream outs) throws IOException { cl_hash(hsize); free_ent = ClearCode + 2; clear_flg = true; output(ClearCode, outs); } void cl_hash(int hsize) { for (int i = 0; i < hsize; ++i) htab[i] = -1; } int a_count; void char_init() { a_count = 0; } byte[] accum = new byte[256]; void char_out(byte c, OutputStream outs) throws IOException { accum[a_count++] = c; if (a_count >= 254) flush_char(outs); } void flush_char(OutputStream outs) throws IOException { if (a_count > 0) { outs.write(a_count); outs.write(accum, 0, a_count); a_count = 0; } } } class IndexGif89Frame extends Gif89Frame { IndexGif89Frame(int width, int height, byte ci_pixels[]) { theWidth = width; theHeight = height; ciPixels = new byte[theWidth * theHeight]; System.arraycopy(ci_pixels, 0, ciPixels, 0, ciPixels.length); } Object getPixelSource() { return ciPixels; } } final class Put { static void ascii(String s, OutputStream os) throws IOException { byte[] bytes = new byte[s.length()]; for (int i = 0; i < bytes.length; ++i) bytes[i] = (byte) s.charAt(i); os.write(bytes); } static void leShort(int i16, OutputStream os) throws IOException { os.write(i16 & 0xff); os.write(i16 >> 8 & 0xff); } } Index: Grapher.java =================================================================== RCS file: /cvsroot/jrobin/src/jrobin/graph/Grapher.java,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** Grapher.java 21 Oct 2003 19:41:59 -0000 1.10 --- Grapher.java 28 Oct 2003 14:51:39 -0000 1.11 *************** *** 133,137 **** // Create the buffered image, get the graphics handle ! BufferedImage bImg = new BufferedImage( imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB ); Graphics2D graphics = (Graphics2D) bImg.getGraphics(); --- 133,138 ---- // Create the buffered image, get the graphics handle ! // BufferedImage bImg = new BufferedImage( imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB); ! BufferedImage bImg = new BufferedImage( imgWidth, imgHeight, BufferedImage.TYPE_BYTE_INDEXED); Graphics2D graphics = (Graphics2D) bImg.getGraphics(); Index: RrdGraph.java =================================================================== RCS file: /cvsroot/jrobin/src/jrobin/graph/RrdGraph.java,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** RrdGraph.java 27 Oct 2003 21:03:33 -0000 1.8 --- RrdGraph.java 28 Oct 2003 14:51:39 -0000 1.9 *************** *** 29,36 **** import java.util.*; ! import java.io.ByteArrayOutputStream; ! import java.io.File; ! import java.io.IOException; ! import java.io.Serializable; import jrobin.core.Util; --- 29,33 ---- import java.util.*; ! import java.io.*; import jrobin.core.Util; *************** *** 43,47 **** * is an excellent free Java library for generating charts and graphs.</p> */ ! public class RrdGraph implements Serializable { private Grapher grapher; --- 40,44 ---- * is an excellent free Java library for generating charts and graphs.</p> */ ! public class RrdGraph implements Serializable { private Grapher grapher; *************** *** 54,62 **** * @throws RrdException Thrown in case of JRobin specific error. */ ! public RrdGraph(RrdGraphDef graphDef) throws IOException, RrdException { grapher = new Grapher( graphDef ); } ! /** --- 51,59 ---- * @throws RrdException Thrown in case of JRobin specific error. */ ! public RrdGraph(RrdGraphDef graphDef) throws IOException, RrdException { grapher = new Grapher( graphDef ); } ! /** *************** *** 68,74 **** // Check, if width/height has changed since last generation // Regenerate the graph ! public BufferedImage getBufferedImage(int width, int height) { ! try { if ( img != null ) --- 65,71 ---- // Check, if width/height has changed since last generation // Regenerate the graph ! public BufferedImage getBufferedImage(int width, int height) { ! try { if ( img != null ) *************** *** 83,87 **** e.printStackTrace(); } ! return null; } --- 80,84 ---- e.printStackTrace(); } ! return null; } *************** *** 94,98 **** * @throws IOException Thrown in case of I/O error. */ ! public void saveAsPNG(String path, int width, int height) throws IOException { Util.time(); --- 91,95 ---- * @throws IOException Thrown in case of I/O error. */ ! public void saveAsPNG(String path, int width, int height) throws IOException { Util.time(); *************** *** 101,107 **** ImageIO.write( r, "png", new File(path) ); Util.time(5); ! } ! /** * Saves graph in JPEG format --- 98,104 ---- ImageIO.write( r, "png", new File(path) ); Util.time(5); ! } ! /** * Saves graph in JPEG format *************** *** 112,116 **** * @throws IOException Thrown in case of I/O error. */ ! public void saveAsJPEG(String path, int width, int height, float quality) throws IOException { // Based on http://javaalmanac.com/egs/javax.imageio/JpegWrite.html?l=rel --- 109,113 ---- * @throws IOException Thrown in case of I/O error. */ ! public void saveAsJPEG(String path, int width, int height, float quality) throws IOException { // Based on http://javaalmanac.com/egs/javax.imageio/JpegWrite.html?l=rel *************** *** 119,123 **** BufferedImage gImage = getBufferedImage(width, height); RenderedImage rndImage = (RenderedImage) gImage; ! // Find a jpeg writer ImageWriter writer = null; --- 116,120 ---- BufferedImage gImage = getBufferedImage(width, height); RenderedImage rndImage = (RenderedImage) gImage; ! // Find a jpeg writer ImageWriter writer = null; *************** *** 143,151 **** writer.dispose(); ios.close(); ! } catch (Exception e) { e.printStackTrace(); } } --- 140,172 ---- writer.dispose(); ios.close(); ! } catch (Exception e) { e.printStackTrace(); } + } + + public void saveAsGIF(String path, int width, int height) { + BufferedImage image = getBufferedImage(width, height); + try { + Gif89Encoder gifEncoder = new Gif89Encoder(image); + FileOutputStream stream = new FileOutputStream(path, false); + gifEncoder.encode(stream); + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public byte[] getGIFBytes(int width, int height) { + BufferedImage image = getBufferedImage(width, height); + ByteArrayOutputStream bStream = new ByteArrayOutputStream(); + try { + Gif89Encoder gifEncoder = new Gif89Encoder(image); + gifEncoder.encode(bStream); + } catch (IOException e) { + e.printStackTrace(); + } + return bStream.toByteArray(); } |