Package org.jrobin.graph

Source Code of org.jrobin.graph.Put

/*******************************************************************************
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
* Copyright (c) 2011 The OpenNMS Group, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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
        ) {
      ;
    }
    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);
  }
}
TOP

Related Classes of org.jrobin.graph.Put

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.