diff options
| -rw-r--r-- | build.properties | 4 | ||||
| -rw-r--r-- | source/de/anomic/search/RankingProcess.java | 2 | ||||
| -rw-r--r-- | source/net/yacy/visualization/AnimGifEncoder.java | 1190 | ||||
| -rw-r--r-- | source/net/yacy/visualization/AnimationGIF.java | 198 |
4 files changed, 201 insertions, 1193 deletions
diff --git a/build.properties b/build.properties index 5f1747692..678872126 100644 --- a/build.properties +++ b/build.properties @@ -1,6 +1,6 @@ # defining some compiler arguments
-javacSource=1.5
-javacTarget=1.5
+javacSource=1.6
+javacTarget=1.6
# Release Configuration
releaseVersion=0.99
diff --git a/source/de/anomic/search/RankingProcess.java b/source/de/anomic/search/RankingProcess.java index f5e2c79c4..97212bfad 100644 --- a/source/de/anomic/search/RankingProcess.java +++ b/source/de/anomic/search/RankingProcess.java @@ -595,7 +595,7 @@ public final class RankingProcess extends Thread { String word;
byte[] termHash;
int c;
- double q, min = Double.MAX_VALUE, max = Double.MIN_NORMAL;
+ double q, min = Double.MAX_VALUE, max = Double.MIN_VALUE;
int ic = count;
while (ic-- > 0 && i.hasNext()) {
word = i.next();
diff --git a/source/net/yacy/visualization/AnimGifEncoder.java b/source/net/yacy/visualization/AnimGifEncoder.java deleted file mode 100644 index 46b6ba44c..000000000 --- a/source/net/yacy/visualization/AnimGifEncoder.java +++ /dev/null @@ -1,1190 +0,0 @@ -/* - * (C)2000 by F. Jalvingh, Mumble Internet Services - * For questions and the like: fjalvingh@bigfoot.com - * - * Compression part (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved. - * - * This software is placed in the public domain. You are free to use this - * software for any means while respecting the above copyright. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Optimizations by Jal: - * --------------------- - * Initial: Coded RLE code for building the 8-bit color table. - * 6dec00: Changed code to remove extraneous if's and unrolled some calls. - * Replaced color hashtable with local specialized variant. - * 7dec00: Made specialized direct buffer access versions for BufferedImage - * images.. - * - */ - -// very slightly adopted by Michael Christen, 12.12.2007 -// - removed unused variables -// - replaced old java classes by new one - -package net.yacy.visualization; - -import java.awt.Canvas; -import java.awt.Image; -import java.awt.MediaTracker; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.DataBufferInt; -import java.awt.image.DataBufferShort; -import java.awt.image.IndexColorModel; -import java.awt.image.PixelGrabber; -import java.awt.image.PixelInterleavedSampleModel; -import java.awt.image.Raster; -import java.awt.image.SampleModel; -import java.awt.image.SinglePixelPackedSampleModel; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; - -import net.yacy.kelondro.logging.Log; - - -/** - * <p>This class can be used to write an animated GIF file by combining several - * images. It is loosely based on the Acme GIF encoder.</p> - * - * <p>The characteristics of the generated Gif 89a image are: - * <ul> - * <li>Only a single color table is used (no local tables). This table is - * created by combining the colors from all other images.</li> - * </ul> - * </p> - * - * @author F. Jalvingh - */ -public class AnimGifEncoder { - /** The default interlacing indicator */ - private boolean m_default_interlace = false; - - /** The default delay time, */ - private final static int m_default_delay = 100; - - /** Set when looping the set is requested. */ - private boolean m_loop = true; - - /** The outputstream to write the image to. */ - private final OutputStream m_os; - - /** The (current) list of images to embed in the GIF */ - private ArrayList<AnIma> m_ima_ar; - - /** The total width and height of all combined images */ - private int m_w, m_h; - - /** The canvas is used to proprly track images. */ - private Canvas m_cv; - - /** The index for the "transparant" color. -1 if no transparant found. */ - private short m_transparant_ix = -1; - - /** The index (palette table entry #) to use for the NEXT color encountered */ - private short m_color_ix; - - /** The #of bits to use (2log m_color_ix). */ - private int m_color_bits; - - /// Temp optimization inhibition. - public boolean m_no_opt; - - /** - * This constructor creates an empty default codec. - */ - public AnimGifEncoder(final OutputStream os) { - m_os = os; - } - - /** - * Creates a codec and specify interlace (not implemented yet). - */ - public AnimGifEncoder(final OutputStream os, final boolean interlace) { - m_os = os; - m_default_interlace = interlace; - } - - /** - * <p>For animated GIF's the default is to LOOP all images in the GIF file. - * This means that after displaying all images in the file the first image - * is redisplayed ad infinitum.</p> - * <p>To prevent the images from looping call setLoop(false) before calling - * the encode() method. - * </p> - * <p>The current version does not allow the number of repetitions to be - * specified. - * </p> - */ - public void setLoop(final boolean loop) { - m_loop = loop; - } - - /** - * Releases ALL cached resources. - */ - public void flush() { - //-- 1. The basic stuff - m_ccolor_ar = null; - m_cindex_ar = null; - m_cv = null; - m_ima_ar = null; - - //-- 2. The compressor. - m_curr_pixels = null; - htab = null; - codetab = null; - accum = null; - } - - /*------------------------------------------------------------------*/ - /* CODING: Adding images to combine into the animated GIF... */ - /*------------------------------------------------------------------*/ - /** - * Adds the specified image to the list of images. While adding, the - * image is converted to pixels; each color is added to the color table - * and the resulting 8-bit pixelset is saved. After this call the image - * is released, and only the pixelset remains until the encode call is - * made. Calling encode will release the pixelset. - */ - public void add(final Image ima, final int delaytime, final boolean interlace, final int px, final int py) throws IOException { - final AnIma ai = new AnIma(); - ai.m_delay = delaytime; - ai.m_interlace = interlace; - ai.m_x = px; - ai.m_y = py; - - //-- Add to the list of images to embed, - if(m_ima_ar == null) // First call? - { - m_ccolor_ar = new int[CHSIZE]; // New colors code table - m_cindex_ar = new short[CHSIZE]; - m_ima_ar = new ArrayList<AnIma>(10); // Contains all component images, - m_cv = new Canvas(); - } - m_ima_ar.add(ai); - - //-- Pre-scan the image!! - if(! m_no_opt) - preCode(ai, ima); // Convert to 8bit and make palette - else - precodeImage(ai, ima); - } - - /** - * Adds the specified image to the list of images. - */ - public void add(final Image ima) throws IOException { - add(ima, m_ima_ar == null ? 0 : m_default_delay, m_default_interlace, 0, 0); - } - - /** - * Adds the specified image to the list of images. - */ - public void add(final Image ima, final int delay) throws IOException { - add(ima, delay, m_default_interlace, 0, 0); - } - - /*------------------------------------------------------------------*/ - /* CODING: I/O to the file - helpers... */ - /*------------------------------------------------------------------*/ - /** - * Writes a string as a #of bytes to the output stream. - */ - private void utStr(final String str) throws IOException { - final byte[] buf = str.getBytes(); - m_os.write( buf ); - } - - private void utWord(final int val) throws IOException { - utByte( (byte) ( val & 0xff)); - utByte( (byte) (( val >> 8 ) & 0xff )); - } - - private void utByte(final byte b) throws IOException { - m_os.write( b ); - } - - /*------------------------------------------------------------------*/ - /* CODING: Starting the encode process... */ - /*------------------------------------------------------------------*/ - /** - * Creates the GIF file from all images added to the encoder. - */ - public void encode() throws IOException { - //-- Check validity, - if(m_ima_ar == null || m_ima_ar.size() == 0) - throw new IOException("No images added."); - - //-- Init the compressor's tables - htab = new int[HSIZE]; - codetab = new int[HSIZE]; - accum = new byte[256]; - - //-- Write the GIF header now, - genHeader(); - - /* - * Traverse the data for each image. This determines the actual color - * table and the complete output size. - */ - for (int i = 0; i < m_ima_ar.size(); i++) { - final AnIma ai = m_ima_ar.get(i); - genImage(ai); - ai.m_rgb = null; - } - genTrailer(); - flush(); - } - - /*--------------------------------------------------------------*/ - /* CODING: Color table code & specialized color hashtable. */ - /*--------------------------------------------------------------*/ - /* - * This is a hashtable mapping (int, byte). The first int is the actual - * color as gotten from the image. The byte is the index color in the - * colormap for the entry. - * We need to find (byte) by indexing with (int) VERY quicky. - * Furthermore we already know that the table will at max hold 256 entries. - * - * Since all colors >= 0 are transparant, we use (int) = 0 as the empty - * case. - * - * This hashtable uses the same hash mechanism as the LZH compressor: a - * double hash without chaining. - */ - private final static int CHSIZE = 1023; - - /// The color hashtable's COLOR table (int rcolors) - private int[] m_ccolor_ar; - - /// The color hashtable's INDEX table (byte index) - private short[] m_cindex_ar; - - /** - * This retrieves the index for a color code from the color hash. If the - * color doesn't exist it is added to the hash table. This uses the double - * hash mechanism described above. If this call causes >255 colors to be - * stored it throws a too many colors exception. - * The function returns the index code for the color. - */ - private short findColorIndex(final int color) throws IOException { - //-- 1. Primary hash.. - int i = (color & 0x7fffffff) % CHSIZE; - - if(m_ccolor_ar[i] == color) // Bucket found? - return m_cindex_ar[i]; - - //-- 2. No match. If the bucket is not empty do the 2nd hash, - if(m_ccolor_ar[i] != 0) // Bucket is full? - { - //-- This was a clash. Locate a new bucket & look for another match! - final int disp = CHSIZE - i; - do - { - i -= disp; - if(i < 0) i += CHSIZE; - if(m_ccolor_ar[i] == color) // Found in 2nd hash? - return m_cindex_ar[i]; // Then return it. - } while(m_ccolor_ar[i] != 0); // Loop till empty bucket. - } - - //-- 3. Empty bucket found: add this there as a new index. - if(m_color_ix >= 256) - throw new IOException("More than 255 colors in this GIF are not allowed."); - m_ccolor_ar[i] = color; - m_cindex_ar[i] = m_color_ix; - return m_color_ix++; - } - - /*--------------------------------------------------------------*/ - /* CODING: Optimized pixel grabbers... */ - /*--------------------------------------------------------------*/ - /** - * Checks if the image lies in the current complete image, else it extends - * the source image. - */ - private void checkTotalSize(final AnIma ai) { - int t; - - t = ai.m_w + ai.m_x; // Get end-X of image, - if(t > m_w) m_w = t; // Adjust complete GIF's size - t = ai.m_h + ai.m_y; // Get total height - if(t > m_h) m_h = t; // Adjust if higher, - } - - /*--------------------------------------------------------------*/ - /* CODING: The precoder translates all to 8bit indexed... */ - /*--------------------------------------------------------------*/ - /** - * Traverse this image, and determine it's characteristics. It adds all - * used colors to the color table and determines the completed size of - * the thing. The image is converted to an 8-bit pixelmap where each pixel - * indexes the generated color table. - * This function tries to get the fastest access to the pixel data for - * several types of BufferedImage. This should enhance the encoding speed - * by preventing the loop thru the entire generalized Raster and ColorModel - * method.... - * All precode methods build a color table containing all colors used in - * the image, and an 8-bit "image" containing, for each pixel, the index - * into that color table. They also set the transparant color to use. - */ - private void preCode(final AnIma ai, final Image ima) throws IOException { - //-- Call the appropriate encoder depending on the image type. - if(ima instanceof BufferedImage) - precodeBuffered(ai, (BufferedImage) ima); - else - precodeImage(ai, ima); - } - - /** - * Tries to decode a buffered image in an optimal way. It checks to see - * if it knows the BufferedImage type and calls the appropriate quick - * decoder. If the image is not implemented we fall back to the generic - * method. - */ - private void precodeBuffered(final AnIma ai, final BufferedImage bi) throws IOException { - //-- 1. Handle all shared tasks... - ai.m_w = bi.getWidth(); - ai.m_h = bi.getHeight(); - if(ai.m_h == 0 || ai.m_w == 0) return; - checkTotalSize(ai); - - //-- 2. Optimize for known types... - boolean done= false; - final int bt = bi.getType(); - switch(bt) - { - case BufferedImage.TYPE_BYTE_INDEXED: done = precodeByteIndexed(ai, bi); break; - case BufferedImage.TYPE_INT_BGR: done = precodeIntPacked(ai, bi); break; - case BufferedImage.TYPE_INT_ARGB: done = precodeIntPacked(ai, bi); break; - case BufferedImage.TYPE_USHORT_555_RGB: done = precodeShortPacked(ai, bi); break; - case BufferedImage.TYPE_USHORT_565_RGB: done = precodeShortPacked(ai, bi); break; - case BufferedImage.TYPE_INT_RGB: done = precodeIntPacked(ai, bi); break; - } - - if(done) return; - - precodeImage(ai, bi); - } - - private int getBiOffset(final Raster ras, final PixelInterleavedSampleModel sm, final int x, final int y) { - return (y-ras.getSampleModelTranslateY()) * sm.getScanlineStride() + x-ras.getSampleModelTranslateX(); - } - - private int getBiOffset(final Raster ras, final SinglePixelPackedSampleModel sm, final int x, final int y) { - return (y-ras.getSampleModelTranslateY()) * sm.getScanlineStride() + x-ras.getSampleModelTranslateX(); - } - - /*--------------------------------------------------------------*/ - /* CODING: BufferedImage.TYPE_BYTE_INDEXED.. */ - /*--------------------------------------------------------------*/ - /** - * Encodes TYPE_BYTE_INDEXED images. - */ - private boolean precodeByteIndexed(final AnIma ai, final BufferedImage bi) throws IOException { - //-- Get the colormodel, the raster, the databuffer and the samplemodel - final ColorModel tcm = bi.getColorModel(); - if(! (tcm instanceof IndexColorModel)) return false; - final IndexColorModel cm = (IndexColorModel) tcm; - - final Raster ras = bi.getRaster(); - final SampleModel tsm = ras.getSampleModel(); - if(! (tsm instanceof PixelInterleavedSampleModel)) return false; - final PixelInterleavedSampleModel sm = (PixelInterleavedSampleModel) tsm; - - final DataBuffer dbt = ras.getDataBuffer(); - if(dbt.getDataType() != DataBuffer.TYPE_BYTE) return false; - if(dbt.getNumBanks() != 1) return false; - final DataBufferByte db = (DataBufferByte) dbt; - - //-- Prepare the color mapping - final short[] map = new short[256]; // Alternate lookup table - for(int i = 0; i < 256; i++) // Set all entries to unused, - map[i] = -1; - - /* - * Prepare the run: get all constants e.a. The mechanism runs thru - * all pixels by traversing each X scanline, then moving to the next - * one. One fun thing: we only have to COPY all pixels, since we're - * already byte-packed. - */ - final int endoff = ai.m_w * ai.m_h; // Output image size, - final byte[] par = new byte[endoff]; // Byte-indexed output array, - int doff = 0; // Destination offset, - - //-- source - int soff = getBiOffset(ras, sm, 0, 0); - final byte[] px = db.getData(0); // Get the pixelset, - final int esoff = getBiOffset(ras, sm, ai.m_w-1, ai.m_h-1); // calc end offset, - final int iw = sm.getScanlineStride(); // Increment width = databuf's width - - while(soff < esoff) { // For all scan lines, - - final int xe = soff + ai.m_w; // End for this line - while(soff < xe) { // While within this line - //-- (continue) collect a run, - final int rs = soff; // Save run start - final byte rcolor = px[soff++]; // First color - while(soff < xe && px[soff] == rcolor) // Run till eoln or badclor - soff++; - - //-- Run ended. Map the input index to the GIF's index, - short ii = map[rcolor + 0x80]; - if (ii == -1){ // Unknown map? - //-- New color. Get it's translated RGB value, - final int rix = rcolor & 0xff; // Translate to unsigned - int rgb = cm.getRGB(rix); // Get RGB value for this input index, - if(rgb >= 0) { // Transparant color? - //-- If there is a transparant color index use it... - if (m_transparant_ix < 0) { - //-- First transparant color found- save it, - if(rgb == 0) rgb = 1; // Zero color protection - req'd for hashtable implementation - m_transparant_ix = findColorIndex(rgb); - } - ii = m_transparant_ix; // Use trans color to fill - } else { - //-- Not transparant, - ii = findColorIndex(rgb); // Add RGB value to the index, - } - map[rcolor + 0x80] = ii; - } - - //-- Always write this run. - final int dep = doff + (soff - rs); // End output pos - final byte idx = (byte) ii; - while(doff < dep) - par[doff++] = idx; // Fill output. - } - - //-- Prepare for a new line. - soff += iw - ai.m_w; // Increment what's left to next line, - } - - ai.m_rgb = par; // Save created thing - return true; - } - - /*--------------------------------------------------------------*/ - /* CODING: BufferedImage.All int packed stuff.. */ - /*--------------------------------------------------------------*/ - /** - * Encodes INT pixel-packed images. - */ - private boolean precodeIntPacked(final AnIma ai, final BufferedImage bi) throws IOException { - //-- Get the colormodel, the raster, the databuffer and the samplemodel - final ColorModel cm = bi.getColorModel(); - final Raster ras = bi.getRaster(); - final SampleModel tsm = ras.getSampleModel(); - if(! (tsm instanceof SinglePixelPackedSampleModel)) return false; - final SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel) tsm; - - final DataBuffer dbt = ras.getDataBuffer(); - if(dbt.getDataType() != DataBuffer.TYPE_INT) return false; - if(dbt.getNumBanks() != 1) return false; - final DataBufferInt db = (DataBufferInt) dbt; - - /* - * Prepare the run: get all constants e.a. The mechanism runs thru - * all pixels by traversing each X scanline, then moving to the next - * one. One fun thing: we only have to COPY all pixels, since we're - * already byte-packed. - */ - final int endoff = ai.m_w * ai.m_h; // Output image size, - final byte[] par = new byte[endoff]; // Byte-indexed output array, - int doff = 0; // Destination offset, - byte ii; - - //-- source - int soff = getBiOffset(ras, sm, 0, 0); - final int[] px = db.getData(0); // Get the pixelset, - final int esoff = getBiOffset(ras, sm, ai.m_w-1, ai.m_h-1); // calc end offset, - final int iw = sm.getScanlineStride(); // Increment width = databuf's width - - while(soff < esoff) { // For all scan lines, - - final int xe = soff + ai.m_w; // End for this line - while (soff < xe) { // While within this line - //-- (continue) collect a run, - final int rs = soff; // Save run start - final int rcolor = px[soff++]; // First color - while(soff < xe && px[soff] == rcolor) // Run till eoln or badclor - soff++; - - //-- Run ended. Map the input index to the GIF's index, - int rgb = cm.getRGB(rcolor); // Get RGB value for this input index, - if(rgb >= 0) { // Transparant color? - //-- If there is a transparant color index use it... - if(m_transparant_ix < 0) { - //-- First transparant color found- save it, - if(rgb == 0) rgb = 1; // Zero color protection - req'd for hashtable implementation - m_transparant_ix = findColorIndex(rgb); - } - ii = (byte)m_transparant_ix; // Use trans color to fill - } else { - //-- Not transparant, - ii = (byte)findColorIndex(rgb); // Add RGB value to the index, - } - - //-- Always write this run. - final int dep = doff + (soff - rs); // End output pos - while(doff < dep) - par[doff++] = ii; // Fill output. - } - - //-- Prepare for a new line. - soff += iw - ai.m_w; // Increment what's left to next line, - - } - - ai.m_rgb = par; // Save created thing - return true; - } - - /*--------------------------------------------------------------*/ - /* CODING: BufferedImage- SHORT type stuff.. */ - /*--------------------------------------------------------------*/ - /** - * Encodes SHORT pixel-packed images. - */ - private boolean precodeShortPacked(final AnIma ai, final BufferedImage bi) throws IOException { - //-- Get the colormodel, the raster, the databuffer and the samplemodel - final ColorModel cm = bi.getColorModel(); - final Raster ras = bi.getRaster(); - final SampleModel tsm = ras.getSampleModel(); - if(! (tsm instanceof SinglePixelPackedSampleModel)) return false; - final SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel) tsm; - - final DataBuffer dbt = ras.getDataBuffer(); - if(dbt.getDataType() != DataBuffer.TYPE_SHORT) return false; - if(dbt.getNumBanks() != 1) return false; - final DataBufferShort db = (DataBufferShort) dbt; - - /* - * Prepare the run: get all constants e.a. The mechanism runs thru - * all pixels by traversing each X scanline, then moving to the next - * one. One fun thing: we only have to COPY all pixels, since we're - * already byte-packed. - */ - final int endoff = ai.m_w * ai.m_h; // Output image size, - final byte[] par = new byte[endoff]; // Byte-indexed output array, - int doff = 0; // Destination offset, - byte ii; - - //-- source - int soff = getBiOffset(ras, sm, 0, 0); - final short[] px = db.getData(0); // Get the pixelset, - final int esoff = getBiOffset(ras, sm, ai.m_w-1, ai.m_h-1); // calc end offset, - final int iw = sm.getScanlineStride(); // Increment width = databuf's width - - while(soff < esoff) // For all scan lines, - { - final int xe = soff + ai.m_w; // End for this line - while(soff < xe) // While within this line - { - //-- (continue) collect a run, - final int rs = soff; // Save run start - final short rcolor = px[soff++]; // First color - while(soff < xe && px[soff] == rcolor) // Run till eoln or badclor - soff++; - - //-- Run ended. Map the input index to the GIF's index, - int rgb = cm.getRGB(rcolor); // Get RGB value for this input index, - if(rgb >= 0) // Transparant color? - { - //-- If there is a transparant color index use it... - if(m_transparant_ix < 0) - { - //-- First transparant color found- save it, - if(rgb == 0) rgb = 1; // Zero color protection - req'd for hashtable implementation - m_transparant_ix = findColorIndex(rgb); - } - ii = (byte)m_transparant_ix; // Use trans color to fill - } - else - { - //-- Not transparant, - ii = (byte)findColorIndex(rgb); // Add RGB value to the index, - } - - //-- Always write this run. - final int dep = doff + (soff - rs); // End output pos - while(doff < dep) - par[doff++] = ii; // Fill output. - } - - //-- Prepare for a new line. - soff += iw - ai.m_w; // Increment what's left to next line, - - } - - ai.m_rgb = par; // Save created thing - return true; - } - - - /*--------------------------------------------------------------*/ - /* CODING: The generic Image stuff to translate the GIF */ - /*--------------------------------------------------------------*/ - /** - * Using a generic Image, this uses a PixelGrabber to get an integer - * pixel array. - */ - private void precodeImage(final AnIma ai, final Image ima) throws IOException { - int[] px; - - //-- Wait for the image to arrive, - MediaTracker mt = new MediaTracker(m_cv); - mt.addImage(ima, 0); - try - { - mt.waitForAll(); // Be use all are loaded, - } - catch(final InterruptedException x) - { - throw new IOException("Interrupted load of image"); - } - mt.removeImage(ima, 0); - mt = null; - - //-- Get the images' size & adjust the complete GIF's size, - ai.m_w = ima.getWidth(m_cv); - ai.m_h = ima.getHeight(m_cv); - if(ai.m_h == 0 || ai.m_w == 0) return; - checkTotalSize(ai); - - //-- Grab pixels & convert to 8-bit pixelset. - final PixelGrabber pg = new PixelGrabber(ima, 0, 0, ai.m_w, ai.m_h, true); - try { - pg.grabPixels(); - } catch(final InterruptedException x) { - throw new IOException("Interrupted load of image"); - } - px = (int[]) pg.getPixels(); // Get the pixels, - - translateColorsByArray(ai, px); // Run the translator - } - - - /** - * For each pixel in the source image, the color is put into the palette - * for the combined GIF. The index of the color is then used in the 8-bit - * pixelset for this image. - */ - private void translateColorsByArray(final AnIma a, final int[] px) throws IOException { - int off; - byte[] par; - final int endoff = a.m_w * a.m_h; // Total #pixels in image - int rstart, rcolor; // Run data. - byte newc; - - //-- Collect runs of pixels of the same color; then handle them; - par = new byte[endoff]; // Allocate output matrix - off = 0; // Output offset, - while(off < endoff) { - //-- Collect the current run of pixels. - rstart = off; - rcolor = px[off++]; // Get 1st pixel of run, - while(off < endoff && px[off] == rcolor) // Fast loop! - off++; - - //-- Translate the color to an index, and handle transparency, - if(rcolor >= 0) // Is this a TRANSPARANT color? - { - //-- If there is a transparant color index use it... - if(m_transparant_ix < 0) - { - //-- First transparant color found- save it, - if(rcolor == 0) rcolor = 1; // Zero color protection - req'd for hashtable implementation - m_transparant_ix = findColorIndex(rcolor); - } - newc = (byte)m_transparant_ix; // Set color to fill run with - } - else - { - //-- Not transparant- is an index known for this color? - final int i = (rcolor & 0x7fffffff) % CHSIZE; - - if(m_ccolor_ar[i] == rcolor) // Bucket found? - newc = (byte)m_cindex_ar[i]; - else - newc = (byte)findColorIndex(rcolor); // Get color index, - } - - //-- Always fill the run with the replaced color, - while(rstart < off) - par[rstart++] = newc; - - //-- This run has been done!! - } - - a.m_rgb = par; // Save completed map; - } - - /** - * Generates the color map by using the color table and creating all - * rgb tables. These are then written to the output. This gets called when - * all images have been added and pre-traversed. - */ - private void genColorTable() throws IOException { - // Turn colors into colormap entries. - final int nelem = 1 << m_color_bits; - final byte[] reds = new byte[nelem]; - final byte[] grns = new byte[nelem]; - final byte[] blus = new byte[nelem]; - - //-- Now enumerate the color table. - for (int i = CHSIZE; --i >= 0;) { // Count backwards (faster) - if(m_ccolor_ar[i] != 0) { // A color was found? - reds[ m_cindex_ar[i] ] = (byte) ( (m_ccolor_ar[i] >> 16) & 0xff); - grns[ m_cindex_ar[i] ] = (byte) ( (m_ccolor_ar[i] >> 8) & 0xff); - blus[ m_cindex_ar[i] ] = (byte) ( m_ccolor_ar[i] & 0xff ); - } - } - - //-- Write the map to the stream, - for (int i = 0; i < nelem; i++) { // Save all elements, - utByte(reds[i]); - utByte(grns[i]); - utByte(blus[i]); - } - } - - /** - * Writes the GIF file header, containing all up to the first image data - * structure: color table, option fields etc. - */ - private void genHeader() throws IOException { - // Figure out how many bits to use. - if(m_color_ix <= 2) - m_color_bits = 1; - else if(m_color_ix <= 4) - m_color_bits = 2; - else if(m_color_ix <= 8) - m_color_bits = 3; - else if(m_color_ix <= 16) - m_color_bits = 4; - else - m_color_bits = 8; - - //-- Start with the headerm - utStr("GIF89a" ); // Gif89a Header: signature & version - - //-- Logical Screen Descriptor Block - utWord(m_w); // Collated width & height of all images - utWord(m_h); - final byte b = (byte)(0xF0 | (m_color_bits-1));// There IS a color map, 8 bits per color source resolution. not sorted, - utByte(b); // Packet fields, - utByte((byte)0); // Background Color Index assumed 0. - utByte((byte)0); // Pixel aspect ratio 1:1: zero always works... - - //-- Now write the Global Color Map. - genColorTable(); - - if (m_loop && m_ima_ar.size() > 1) { - //-- Generate a Netscape loop thing, - utByte((byte) 0x21); - utByte((byte) 0xff); - utByte((byte) 0x0b); - utStr("NETSCAPE2.0"); - utByte((byte) 0x03); - utByte((byte) 1); - utWord(0); // Repeat indefinitely - utByte((byte)0); - } - } - - /** - * Writes the GIF file trailer, terminating the GIF file. - */ - private void genTrailer() throws IOException { - // Write the GIF file terminator - utByte((byte) ';'); - } - - /** - * Writes a single image instance. - */ - private void genImage(final AnIma ai) throws IOException { - //-- Write out a Graphic Control Extension for transparent colour & repeat, if necessary, - if(m_transparant_ix != -1 || m_ima_ar.size() > 1) { - byte transpar; - - utByte( (byte) '!'); // 0x21 Extension Introducer - utByte( (byte) 0xf9); // Graphic Control Label - utByte( (byte) 4); // Block Size, - if(m_transparant_ix >= 0) { // There IS transparancy? - utByte((byte) 1); // TRANS flag SET - transpar = (byte) m_transparant_ix; - } else { - utByte((byte) 0); // TRANS flag CLEAR - transpar = 0; - } - utWord( ai.m_delay ); // Delay time, - utByte(transpar); // And save the index, - utByte( (byte) 0); - } - - //-- Write the Image Descriptor - utByte((byte)','); - utWord(ai.m_x); // Image left position, - utWord(ai.m_y); // Image right position - utWord(ai.m_w); - utWord(ai.m_h); // And it's size, - utByte((byte) (ai.m_interlace ? 0x40 : 0)); // Packed fields: interlaced Y/N, no local table no sort, - - //-- The table-based image data... - final int initcodesz = m_color_bits <= 1 ? 2 : m_color_bits; - utByte((byte) initcodesz); // Output initial LZH code size, min. 2 bits, - genCompressed(ai, initcodesz+1); // Generate the compressed data, - utByte((byte) 0); // Zero-length packet (end series) - } - - /*------------------------------------------------------------------*/ - /* CODING: Stuff to compress!!! */ - /*------------------------------------------------------------------*/ - /* - * Most of this compressor code has been reaped from the ACME GifEncoder - * package. See there for more details. - * This code will be revised for speed in the next release though. - */ - /** Pixmap from ima currently compressed */ - private byte[] m_curr_pixels; - - /** Current pixel source index in above map */ - private int m_px_ix; - - /** End index within above index. */ - private int m_px_endix; - - private void genCompressed(final AnIma a, final int initcodesz) throws IOException { - //-- Set all globals to retrieve pixel data quickly. $$TODO: Interlaced - m_curr_pixels = a.m_rgb; - m_px_ix = 0; - m_px_endix = a.m_w * a.m_h; // Last index, - - //-- Coder variables. - int i, c, ent, disp, hsize_reg, hshift, fcode; - - //-- Init: the bit-code writer's variables, - cur_accum = 0; - cur_bits = 0; - free_ent = 0; - clear_flg = false; - maxbits = BITS; // user settable max # bits/code - maxmaxcode = 1 << BITS; // should NEVER generate this code - a_count = 0; - g_init_bits = initcodesz; // Initial #of bits - - // Set up the necessary values - clear_flg = false; - n_bits = g_init_bits; - maxcode = MAXCODE( n_bits ); - ClearCode = 1 << ( initcodesz - 1 ); - EOFCode = ClearCode + 1; - free_ent = ClearCode + 2; - char_init(); - - hshift = 0; - for ( fcode = hsize; fcode < 65536; fcode *= 2 ) - ++hshift; - hshift = 8 - hshift; // set hash code range bound - - hsize_reg = hsize; - cl_hash( hsize_reg ); // clear hash table - output(ClearCode); - - ent = m_curr_pixels[m_px_ix++]; // Get 1st pixel value, - outer_loop: while(m_px_ix < m_px_endix) // While not at end - { - c = m_curr_pixels[m_px_ix++]; // Get next pixel value, - fcode = ( c << maxbits ) + ent; - i = ( c << hshift ) ^ ent; // xor hashing - - if(htab[i] == fcode) - { - ent = codetab[i]; - continue; - } - else if ( htab[i] >= 0 ) // non-empty slot - { - disp = hsize_reg - i; // secondary hash (after G. Knott) - if ( i == 0 ) // ?? Should be inpossible?? JAL - 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); - ent = c; - if ( free_ent < maxmaxcode ) - { - codetab[i] = free_ent++; // code -> hashtable - htab[i] = fcode; - } - else - cl_block(); - } - // Put out the final code. - output(ent); - outputEOF(); - } - - static final int EOF = -1; - - // GIFCOMPR.C - GIF Image compression routines - // - // Lempel-Ziv compression based on 'compress'. GIF modifications by - // David Rowley (mgardi@watdcsu.waterloo.edu) - - // General DEFINEs - - static final int BITS = 12; - static final int HSIZE = 5003; // 80% occupancy - - // GIF Image compression - modified 'compress' - // - // Based on: compress.c - File compression ala IEEE Computer, June 1984. - // - // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) - // Jim McKie (decvax!mcvax!jim) - // Steve Davies (decvax!vax135!petsd!peora!srd) - // Ken Turkowski (decvax!decwrl!turtlevax!ken) - // James A. Woods (decvax!ihnp4!ames!jaw) - // Joe Orost (decvax!vax135!petsd!joe) - - int n_bits; // number of bits/code - int maxbits = BITS; // user settable max # bits/code - int maxcode; // maximum code, given n_bits - int maxmaxcode = 1 << BITS; // should NEVER generate this code - - final int MAXCODE( final int n_bits ) { - return ( 1 << n_bits ) - 1; - } - - int[] htab; - int[] codetab; - - int hsize = HSIZE; // for dynamic table sizing - - int free_ent = 0; // first unused entry - - // block compression parameters -- after all codes are used up, - // and compression rate changes, start over. - boolean clear_flg = false; - - // Algorithm: use open addressing double hashing (no chaining) on the - // prefix code / next character combination. We do a variant of Knuth's - // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime - // secondary probe. Here, the modular division first probe is gives way - // to a faster exclusive-or manipulation. Also do block compression with - // an adaptive reset, whereby the code table is cleared when the compression - // ratio decreases, but after the table fills. The variable-length output - // codes are re-sized at this point, and a special CLEAR code is generated - // for the decompressor. Late addition: construct the table according to - // file size for noticeable speed improvement on small files. Please direct - // questions about this implementation to ames!jaw. - - int g_init_bits; - int ClearCode; - int EOFCode; - - // Output the given code. - // Inputs: - // code: A n_bits-bit integer. If == -1, then EOF. This assumes - // that n_bits =< wordsize - 1. - // Outputs: - // Outputs code to the file. - // Assumptions: - // Chars are 8 bits long. - // Algorithm: - // Maintain a BITS character long buffer (so that 8 codes will - // fit in it exactly). Use the VAX insv instruction to insert each - // code in turn. When the buffer fills up empty it and start over. - - int cur_accum = 0; - int cur_bits = 0; - - static int masks[] = { - 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, - 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, - 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - - void output(final int code) throws IOException { - cur_accum |= ( code << cur_bits ); - cur_bits += n_bits; - - while( cur_bits >= 8 ) { - //-- Expanded char_out code - accum[a_count++] = (byte) cur_accum; - if ( a_count >= 254 ) - flush_char(); - //-- End of char_out expansion - - cur_accum >>= 8; - cur_bits -= 8; - } - - // If the next entry is going to be too big for the code size, - // then increase it, if possible. - // $$Rewrote if (JAL) - if(clear_flg) { - maxcode = MAXCODE(n_bits = g_init_bits); - clear_flg = false; - } else if(free_ent > maxcode) { - ++n_bits; - - if (n_bits == maxbits) - maxcode = maxmaxcode; - else - maxcode = MAXCODE(n_bits); - } - } - - /** - * Removed from output() above to skip an extra IF in the main loop. Must - * be called instead of calling output(EOFCode). - */ - private void outputEOF() throws IOException { - output(EOFCode); // Actually output the code - - //-- At EOF, write the rest of the buffer. - while( cur_bits > 0) - { - //-- Expanded char_out. - accum[a_count++] = (byte) cur_accum; - if ( a_count >= 254 ) - flush_char(); - //-- End of char_out expansion - cur_accum >>= 8; - cur_bits -= 8; - } - flush_char(); - } - - // Clear out the hash table - // table clear for block compress - void cl_block() throws IOException { - cl_hash( hsize ); - free_ent = ClearCode + 2; - clear_flg = true; - - output(ClearCode); - } - - // reset code table - void cl_hash( final int hsize ) { - for(int i = hsize; --i >= 0;) - htab[i] = -1; - } - - // GIF Specific routines - - // Number of characters so far in this 'packet' - int a_count; - - // Set up the 'byte output' routine - void char_init() { - a_count = 0; - } - - // Define the storage for the packet accumulator - byte[] accum; - - // Add a character to the end of the current packet, and if it is 254 - // characters, flush the packet to disk. - void char_out(final byte c) throws IOException { - accum[a_count++] = c; - if ( a_count >= 254 ) - flush_char(); - } - - // Flush the packet to disk, and reset the accumulator - void flush_char() throws IOException { - if( a_count > 0) { - m_os.write( a_count ); - m_os.write( accum, 0, a_count ); - a_count = 0; - } - } - - // test method for ymage classes - public static void main(final String[] args) { - System.setProperty("java.awt.headless", "true"); - - final RasterPlotter m = new RasterPlotter(200, 300, RasterPlotter.DrawMode.MODE_SUB, "FFFFFF"); - RasterPlotter.demoPaint(m); - final File file = new File("/Users/admin/Desktop/testimage.gif"); - - OutputStream os; - try { - os = new FileOutputStream(file); - final AnimGifEncoder age = new AnimGifEncoder(os); - age.add(m.getImage()); - age.add(m.getImage()); - age.encode(); - os.close(); - } catch (final FileNotFoundException e) { - Log.logException(e); - } catch (final IOException e) { - Log.logException(e); - } - } -} - -class GifColorEntry { - /** The actual RGB color for this entry */ - public int m_color; - - /** The colortable [palette] entry number for this color */ - public int m_index; - - public GifColorEntry(final int col, final int ix) { - m_color = col; - m_index = ix; - } -}; - -class AnIma { - /** This-image's interlace flag */ - public boolean m_interlace; - - /** This-image's delay factor */ - public int m_delay; - - /** This-image's source and destination within the completed image */ - public int m_x, m_y; - - /** This image's width and height */ - public int m_w, m_h; - - /** This-image's 8-bit pixelset. It indexes the m_color_ar table. */ - public byte[] m_rgb; -}; diff --git a/source/net/yacy/visualization/AnimationGIF.java b/source/net/yacy/visualization/AnimationGIF.java new file mode 100644 index 000000000..06f69b047 --- /dev/null +++ b/source/net/yacy/visualization/AnimationGIF.java @@ -0,0 +1,198 @@ +/** + * AnimationGIF + * Copyright 2010 by Michael Christen + * First released 20.11.2010 at http://yacy.net + * + * $LastChangedDate: 2010-06-16 17:11:21 +0200 (Mi, 16 Jun 2010) $ + * $LastChangedRevision: 6974 $ + * $LastChangedBy: orbiter $ + * + * 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 program in the file lgpl21.txt + * If not, see <http://www.gnu.org/licenses/>. + */ + +package net.yacy.visualization; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Random; + +import javax.imageio.IIOImage; +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import javax.imageio.metadata.IIOInvalidTreeException; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageOutputStream; +import javax.imageio.stream.MemoryCacheImageOutputStream; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +public class AnimationGIF { + + private final static String formatName = "javax_imageio_gif_image_1.0"; + private final static String aesNodeName = "ApplicationExtensions"; + private final static String aeNodeName = "ApplicationExtension"; + private final static String gceNodeName = "GraphicControlExtension"; + private final static String delayNodeName = "delayTime"; + + private int counter, loops; + private IIOMetadata iiom; + private ImageWriter writer; + private ImageWriteParam iwp; + private ImageOutputStream ios; + private ByteArrayOutputStream baos; + + /** + * create a gif animation producer + * @param loops - number of loops for the animated images. -1 = no loops; 0 = indefinitely loops; else: number of loops + */ + public AnimationGIF(int loops) { + this.counter = 0; + this.loops = loops; + this.ios = null; + this.writer = null; + + this.baos = new ByteArrayOutputStream(); + Iterator<ImageWriter> writerIterator = ImageIO.getImageWritersByFormatName("GIF"); + this.writer = writerIterator.next(); // com.sun.media.imageioimpl.plugins.gif.GIFImageWriter, com.sun.imageio.plugins.gif.GIFImageWriter + this.ios = new MemoryCacheImageOutputStream(baos); + this.writer.setOutput(ios); + this.iwp = writer.getDefaultWriteParam(); + } + + /** + * add an image to the animation + * @param image the image + * @param duration the frame time of the image in milliseconds + * @throws IOException + */ + public void addImage(RenderedImage image, int duration) throws IOException { + if (this.counter == 0) { + iiom = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), iwp); + writer.prepareWriteSequence(writer.getDefaultStreamMetadata(iwp)); + } + if (this.counter == 0 && loops >= 0) { + IIOMetadata imageMetadata2 = writer.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), iwp); + try { + setDelay(imageMetadata2, duration); + setLoops(imageMetadata2, this.loops); + writer.writeToSequence(new IIOImage(image, null, imageMetadata2), iwp); + } catch (IIOInvalidTreeException e) { + throw new IOException(e.getMessage()); + } + } else try { + setDelay(iiom, duration); + writer.writeToSequence(new IIOImage(image, null, iiom), iwp); + } catch (IIOInvalidTreeException e) { + throw new IOException(e.getMessage()); + } + this.counter++; + } + + /** + * produce the gif image as byte array + * @return the gif image + */ + public byte[] get() { + if (ios != null) try { + ios.close(); + ios = null; + } catch (IOException e) {} + if (writer != null) { + writer.dispose(); + writer = null; + } + return baos.toByteArray(); + } + + private static void setDelay(IIOMetadata metaData, int delay) throws IIOInvalidTreeException { + Node tree = metaData.getAsTree(formatName); + NodeList nodeList = tree.getChildNodes(); + Node gceNode = null; + for (int i = 0, len = nodeList.getLength(); i < len; i++) { + Node curNode = nodeList.item(i); + if (curNode.getNodeName().equals(gceNodeName)) {gceNode = curNode; break;} + } + if (gceNode == null) throw new IIOInvalidTreeException("Invalid image metadata, could not find " + gceNodeName + "node.", null, tree); + Node delayNode = gceNode.getAttributes().getNamedItem(delayNodeName); + if (delayNode == null) { + delayNode = tree.getOwnerDocument().createAttribute(delayNodeName); + gceNode.appendChild(delayNode); + } + delayNode.setNodeValue(Integer.valueOf(delay / 10).toString()); + metaData.setFromTree(formatName, tree); + } + + /** + * set number of loops for this animation + * @param metaData + * @param loops - 0 = loop continuously; 1-65535 = a specific number of loops + * @throws IIOInvalidTreeException + */ + private static void setLoops(IIOMetadata metaData, int loops) throws IIOInvalidTreeException { + Node tree = metaData.getAsTree(formatName); + IIOMetadataNode aes = new IIOMetadataNode(aesNodeName); + IIOMetadataNode ae = new IIOMetadataNode(aeNodeName); + ae.setAttribute("applicationID", "NETSCAPE"); + ae.setAttribute("authenticationCode", "2.0"); + ae.setUserObject(new byte[]{0x1, (byte) (loops & 0xFF), (byte) ((loops >> 8) & 0xFF)}); + aes.appendChild(ae); + tree.appendChild(aes); + metaData.setFromTree(formatName, tree); + } + + /** + * test image generator + */ + private static RenderedImage generateTestImage(int width, int height, Random r, double angle) { + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics(); + g.setColor(Color.white); g.fillRect(0, 0, width, height); g.setColor(Color.BLUE); + int x = width / 2; + int y = height / 2; + int radius = Math.min(x, y); + g.drawLine(x, y, x + (int) (radius * Math.cos(angle)), y + (int) (radius * Math.sin(angle))); + g.drawString("giftest", r.nextInt(width), r.nextInt(height)); + return img; + } + + public static void main(String[] args) { + System.setProperty("java.awt.headless", "true"); // go into headless awt mode + Random r = new Random(System.currentTimeMillis()); + int framescount = 100; + AnimationGIF generator = new AnimationGIF(0); + try { + for (int i = 0; i < framescount; i++) { + generator.addImage(generateTestImage(320, 160, r, i * 2 * Math.PI / framescount), 10); + } + FileOutputStream fos = new FileOutputStream(new File("/tmp/giftest.gif")); + fos.write(generator.get()); + fos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} |
