X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Ftranslators%2FGIF.java;h=26b1a4021f859ff559a8833757114c7b379cf8e1;hb=4052bf6501ced1a941a66108e87262da033f33af;hp=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391;hpb=167e1c09923897c956b31723211ea890c105c75d;p=org.ibex.core.git diff --git a/src/org/ibex/translators/GIF.java b/src/org/ibex/translators/GIF.java index e69de29..26b1a40 100644 --- a/src/org/ibex/translators/GIF.java +++ b/src/org/ibex/translators/GIF.java @@ -0,0 +1,446 @@ +/* + * This file was adapted from D J Hagberg's GifDecoder.java + * + * This software is copyrighted by D. J. Hagberg, Jr., and other parties. + * The following terms apply to all files associated with the software + * unless explicitly disclaimed in individual files. + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + * + * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + * MODIFICATIONS. + * + * GOVERNMENT USE: If you are acquiring this software on behalf of the + * U.S. government, the Government shall have only "Restricted Rights" + * in the software and related documentation as defined in the Federal + * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you + * are acquiring the software on behalf of the Department of Defense, the + * software shall be classified as "Commercial Computer Software" and the + * Government shall have only "Restricted Rights" as defined in Clause + * 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the + * authors grant the U.S. Government and others acting in its behalf + * permission to use and distribute the software in accordance with the + * terms specified in this license. + * + */ +package org.ibex.translators; + +import org.ibex.*; +import org.ibex.util.*; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.PrintWriter; + +/** Converts an InputStream carrying a GIF image into an ARGB int[] */ +public class GIF { + + // Public Methods ///////////////////////////////////////////////////////// + + private GIF() { } + + private static Queue instances = new Queue(10); + + public static void load(InputStream is, Picture p) { + GIF g = (GIF)instances.remove(false); + if (g == null) g = new GIF(); + try { + g._load(is, p); + } catch (Exception e) { + if (Log.on) Log.info(GIF.class, e); + return; + } + // FIXME: must reset fields + // if (instances.size() < 10) instances.append(g); + } + + private void _load(InputStream is, Picture p) throws IOException { + this.p = p; + if (is instanceof BufferedInputStream) _in = (BufferedInputStream)is; + else _in = new BufferedInputStream(is); + decodeAsBufferedImage(0); + p.isLoaded = true; + p = null; + _in = null; + } + + + /** Decode a particular frame from the GIF file. Frames must be decoded in order, starting with 0. */ + private void decodeAsBufferedImage(int page) throws IOException, IOException { + + // If the user requested a page already decoded, we cannot go back. + if (page <= index ) return; + + // If we just started reading this stream, initialize the global info. + if (index < 0 ) { + if (!readIntoBuf(6) || _buf[0] != 'G' || _buf[1] != 'I' || _buf[2] != 'F' || + _buf[3] != '8' || !(_buf[4] == '7' || _buf[4] == '9') || _buf[5] != 'a') + throw new IOException("Not a GIF8Xa file."); + if (!readGlobalImageDescriptor()) throw new IOException("Unable to read GIF header."); + } + + // Loop through the blocks in the image. + int block_identifier; + while (true) { + if(!readIntoBuf(1)) throw new IOException("Unexpected EOF(1)"); + block_identifier = _buf[0]; + if (block_identifier == ';') throw new IOException("No image data"); + if (block_identifier == '!') { + if (!readExtensionBlock()) throw new IOException("Unexpected EOF(2)"); + continue; + } + + // not a valid start character -- ignore it. + if (block_identifier != ',') continue; + + if (!readLocalImageDescriptor()) throw new IOException("Unexpected EOF(3)"); + p.data = new int[p.width * p.height]; + readImage(); + + // If we did not decode the requested index, need to go on + // to the next one (future implementations should consider + // caching already-decoded images here to allow for random + // access to animated GIF pages). + index++; + if (index < page) continue; + + // If we did decode the requested index, we can return. + break; + } + + // Return the image thus-far decoded + return; + } + + /** Actually read the image data */ + private void readImage() + throws IOException, IOException { + int len = p.width; + int rows = p.height; + int initialCodeSize; + int v; + int xpos = 0, ypos = 0, pass = 0, i; + int prefix[] = new int[(1 << MAX_LWZ_BITS)]; + int append[] = new int[(1 << MAX_LWZ_BITS)]; + int stack[] = new int[(1 << MAX_LWZ_BITS)*2]; + int top_idx; + int codeSize, clearCode, inCode, endCode, oldCode, maxCode, code, firstCode; + + // Initialize the decoder + if (!readIntoBuf(1)) throw new IOException("Unexpected EOF decoding image"); + initialCodeSize = _buf[0]; + + // Look at the right color map, setting up transparency if called for. + int[] cmap = global_color_map; + if (hascmap) cmap = color_map; + if (trans_idx >= 0) cmap[trans_idx] = 0x00000000; + + /* Initialize the decoder */ + /* Set values for "special" numbers: + * clear code reset the decoder + * end code stop decoding + * code size size of the next code to retrieve + * max code next available table position + */ + clearCode = 1 << initialCodeSize; + endCode = clearCode + 1; + codeSize = initialCodeSize + 1; + maxCode = clearCode + 2; + oldCode = -1; + firstCode = -1; + + for (i = 0; i < clearCode; i++) append[i] = i; + top_idx = 0; // top of stack. + + bitsInWindow = 0; + bytes = 0; + window = 0L; + done = false; + c = -1; + + /* Read until we finish the image */ + ypos = 0; + for (i = 0; i < rows; i++) { + for (xpos = 0; xpos < len;) { + + if (top_idx == 0) { + /* Bummer -- our stack is empty. Now we have to work! */ + code = getCode(codeSize); + if (code < 0) return; + + if (code > maxCode || code == endCode) return; + if (code == clearCode) { + codeSize = initialCodeSize + 1; + maxCode = clearCode + 2; + oldCode = -1; + continue; + } + + // Last pass reset the decoder, so the first code we + // see must be a singleton. Seed the stack with it, + // and set up the old/first code pointers for + // insertion into the string table. We can't just + // roll this into the clearCode test above, because + // at that point we have not yet read the next code. + if (oldCode == -1) { + stack[top_idx++] = append[code]; + oldCode = code; + firstCode = code; + continue; + } + + inCode = code; + + // maxCode is always one bigger than our + // highest assigned code. If the code we see + // is equal to maxCode, then we are about to + // add a new string to the table. ??? + if (code == maxCode) { + stack[top_idx++] = firstCode; + code = oldCode; + } + + // Populate the stack by tracing the string in the + // string table from its tail to its head + while (code > clearCode) { + stack[top_idx++] = append[code]; + code = prefix[code]; + } + firstCode = append[code]; + + // If there's no more room in our string table, quit. + // Otherwise, add a new string to the table + if (maxCode >= (1 << MAX_LWZ_BITS)) return; + + // Push the head of the string onto the stack + stack[top_idx++] = firstCode; + + // Add a new string to the string table + prefix[maxCode] = oldCode; + append[maxCode] = firstCode; + maxCode++; + + // maxCode tells us the maximum code value we can accept. + // If we see that we need more bits to represent it than + // we are requesting from the unpacker, we need to increase + // the number we ask for. + if ((maxCode >= (1 << codeSize)) && (maxCode < (1<= rows) { + pass++; + if (pass > 3) return; + ypos = _interlaceStart[pass]; + } + } else ypos++; + } + return; + } + + /** Extract the next compression code from the file. */ + private int getCode(int code_size) throws IOException { + int ret; + + while (bitsInWindow < code_size) { + // Not enough bits in our window to cover the request + if (done) return -1; + + if (bytes == 0) { + // Not enough bytes in our buffer to add to the window + bytes = getDataBlock(); + c = 0; + if (bytes <= 0) { + done = true; + break; + } + } + // Tack another byte onto the window, see if that's enough + window += (_buf[c]) << bitsInWindow; + ++c; + bitsInWindow += 8; + bytes--; + } + + + // The next code will always be the last code_size bits of the window + ret = ((int)window) & ((1 << code_size) - 1); + + // Shift data in the window to put the next code at the end + window >>= code_size; + bitsInWindow -= code_size; + return ret; + } + + /** Read the global image descriptor and optional global color map. Sets global_* variables. */ + private boolean readGlobalImageDescriptor() throws IOException { + int packed; + int aspect; // we ignore this. + int ofs; + + if (!readIntoBuf(7) ) return false; + global_width = _buf[0] | (_buf[1] << 8); + global_height = _buf[2] | (_buf[3] << 8); + packed = _buf[4]; + global_bgcolor = _buf[5]; + aspect = _buf[6]; + global_cmapsize = 2 << (packed & 0x07); + global_hascmap = (packed & GLOBALCOLORMAP) == GLOBALCOLORMAP; + global_color_map = null; + + // Read the color map, if we have one. + if (global_hascmap) { + if (!readColorMap(global_cmapsize,true)) { + return false; + } + } + + return true; + } + + /** Read a local image descriptor and optional local color map. */ + private boolean readLocalImageDescriptor() throws IOException { + int packed; + + if (!readIntoBuf(9) ) return false; + + left = _buf[0] | (_buf[1] << 8); + top = _buf[2] | (_buf[3] << 8); + p.width = _buf[4] | (_buf[5] << 8); + p.height = _buf[6] | (_buf[7] << 8); + packed = _buf[8]; + hascmap = (packed & LOCALCOLORMAP) == LOCALCOLORMAP; + cmapsize = 2 << (packed & 0x07); + interlaced = (packed & INTERLACE) == INTERLACE; + color_map = null; + + // Read the local color table, if there is one. + return !(hascmap && !readColorMap(cmapsize,false)); + } + + /** Read a color map (global or local). */ + private boolean readColorMap(int nColors, boolean isGlobal) + throws IOException { + int[] map = new int[nColors]; + for( int i=0; i < nColors; ++i) { + if (!readIntoBuf(3) ) return false; + map[i] = (_buf[0] << 16) | (_buf[1] << 8) | _buf[2] | 0xFF000000; + } + if (isGlobal) global_color_map = map; + else color_map = map; + return true; + } + + /** Read the contents of a GIF89a Graphical Extension Block. */ + private boolean readExtensionBlock() throws IOException { + if (!readIntoBuf(1) ) return false; + int label = _buf[0]; + int count = -1; + switch (label) { + case 0x01: // Plain Text Extension + case 0xff: // Application Extension + case 0xfe: // Comment Extension + break; + case 0xf9: // Graphic Control Extension + count = getDataBlock(); + if (count < 0) return true; + // Check for transparency setting. + if ((_buf[0] & HASTRANSPARENCY) != 0) trans_idx = _buf[3]; + else trans_idx = -1; + } + do { count = getDataBlock(); } while (count > 0); + return true; + } + + /** Read a block of data from the GIF file. */ + private int getDataBlock() throws IOException { + if (!readIntoBuf(1) ) return -1; + int count = _buf[0]; + if (count != 0) if (!readIntoBuf(count) ) return -1; + return count; + } + + /** Read the indicated number of bytes into _buf, our instance-wide buffer. */ + private boolean readIntoBuf(int count) throws IOException { + for(int i = 0; i < count; i++) if ((_buf[i] = _in.read()) == -1) return false; + return true; + } + + // Private Data ////////////////////////////////////////////////////////// + + private Picture p; + + // State management stuff + private int index = -1; + private BufferedInputStream _in = null; + private int[] _buf = new int[BUFSIZE]; + + // Transparency settings + private int trans_idx = -1; + + // Global image descriptor contents + private int global_width = 0; + private int global_height = 0; + private int global_bgcolor = 0; + private int global_cmapsize = 0; + private boolean global_hascmap = false; + private int[] global_color_map = null; + + // Local image descriptor contents + private int left = 0; + private int top = 0; + private int cmapsize = 0; + private boolean hascmap = false; + private boolean interlaced = false; + private int[] color_map = null; + + // Variables used in getCode(...) to track sliding bit-window. + private int bytes = 0; + private boolean done; + private int c; + private long window; + private int bitsInWindow = 0; + + // Class-wide constants. + private static final int INTERLACE = 0x40; + private static final int GLOBALCOLORMAP = 0x80; + private static final int LOCALCOLORMAP = 0x80; + private static final int HASTRANSPARENCY = 0x01; + private static final int MAX_LWZ_BITS = 12; + private static final int BUFSIZE = 280; + private static final int[] _interlaceStep = { 8, 8, 4, 2 }; + private static final int[] _interlaceStart = { 0, 4, 2, 1 }; +} + + +