📄 gifencoder.java
字号:
/* ============================================================
* JRobin : Pure java implementation of RRDTool's functionality
* ============================================================
*
* Project Info: http://www.jrobin.org
* Project Lead: Sasa Markovic (saxon@jrobin.org)
*
* Developers: Sasa Markovic (saxon@jrobin.org)
*
*
* (C) Copyright 2003-2005, by Sasa Markovic.
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
///////////////////////////////////////////////////////////////////
// GifEncoder from J.M.G. Elliott
// http://jmge.net/java/gifenc/
///////////////////////////////////////////////////////////////////
package org.jrobin.graph;
import java.awt.*;
import java.awt.image.PixelGrabber;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Vector;
class GifEncoder {
private Dimension dispDim = new Dimension(0, 0);
private GifColorTable colorTable;
private int bgIndex = 0;
private int loopCount = 1;
private String theComments;
private Vector<Gif89Frame> vFrames = new Vector<Gif89Frame>();
GifEncoder() {
colorTable = new GifColorTable();
}
GifEncoder(Image static_image) throws IOException {
this();
addFrame(static_image);
}
GifEncoder(Color[] colors) {
colorTable = new GifColorTable(colors);
}
GifEncoder(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) ? 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) {
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) {
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -