--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/native/sun/awt/image/gif/gifdecoder.c Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,471 @@
+/*
+ * Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include <stdio.h>
+#include "jni.h"
+#include "jni_util.h"
+
+#define OUTCODELENGTH 4097
+
+/* We use Get/ReleasePrimitiveArrayCritical functions to avoid
+ * the need to copy buffer elements.
+ *
+ * MAKE SURE TO:
+ *
+ * - carefully insert pairs of RELEASE_ARRAYS and GET_ARRAYS around
+ * callbacks to Java.
+ * - call RELEASE_ARRAYS before returning to Java.
+ *
+ * Otherwise things will go horribly wrong. There may be memory leaks,
+ * excessive pinning, or even VM crashes!
+ *
+ * Note that GetPrimitiveArrayCritical may fail!
+ */
+
+#define GET_ARRAYS() \
+ prefix = (short *) \
+ (*env)->GetPrimitiveArrayCritical(env, prefixh, 0); \
+ if (prefix == 0) \
+ goto out_of_memory; \
+ suffix = (unsigned char *) \
+ (*env)->GetPrimitiveArrayCritical(env, suffixh, 0); \
+ if (suffix == 0) \
+ goto out_of_memory; \
+ outCode = (unsigned char *) \
+ (*env)->GetPrimitiveArrayCritical(env, outCodeh, 0); \
+ if (outCode == 0) \
+ goto out_of_memory; \
+ rasline = (unsigned char *) \
+ (*env)->GetPrimitiveArrayCritical(env, raslineh, 0); \
+ if (rasline == 0) \
+ goto out_of_memory; \
+ block = (unsigned char *) \
+ (*env)->GetPrimitiveArrayCritical(env, blockh, 0); \
+ if (block == 0) \
+ goto out_of_memory
+
+/*
+ * Note that it is important to check whether the arrays are NULL,
+ * because GetPrimitiveArrayCritical might have failed.
+ */
+#define RELEASE_ARRAYS() \
+if (prefix) \
+ (*env)->ReleasePrimitiveArrayCritical(env, prefixh, prefix, 0); \
+if (suffix) \
+ (*env)->ReleasePrimitiveArrayCritical(env, suffixh, suffix, 0); \
+if (outCode) \
+ (*env)->ReleasePrimitiveArrayCritical(env, outCodeh, outCode, 0); \
+if (rasline) \
+ (*env)->ReleasePrimitiveArrayCritical(env, raslineh, rasline, 0); \
+if (block) \
+ (*env)->ReleasePrimitiveArrayCritical(env, blockh, block, 0)
+
+/* Place holders for the old native interface. */
+
+long
+sun_awt_image_GifImageDecoder_parseImage()
+{
+ return 0;
+}
+
+void
+sun_awt_image_GifImageDecoder_initIDs()
+{
+}
+
+static jmethodID readID;
+static jmethodID sendID;
+static jfieldID prefixID;
+static jfieldID suffixID;
+static jfieldID outCodeID;
+
+JNIEXPORT void JNICALL
+Java_sun_awt_image_GifImageDecoder_initIDs(JNIEnv *env, jclass this)
+{
+ readID = (*env)->GetMethodID(env, this, "readBytes", "([BII)I");
+ sendID = (*env)->GetMethodID(env, this, "sendPixels",
+ "(IIII[BLjava/awt/image/ColorModel;)I");
+ prefixID = (*env)->GetFieldID(env, this, "prefix", "[S");
+ suffixID = (*env)->GetFieldID(env, this, "suffix", "[B");
+ outCodeID = (*env)->GetFieldID(env, this, "outCode", "[B");
+}
+
+JNIEXPORT jboolean JNICALL
+Java_sun_awt_image_GifImageDecoder_parseImage(JNIEnv *env,
+ jobject this,
+ jint relx, jint rely,
+ jint width, jint height,
+ jint interlace,
+ jint initCodeSize,
+ jbyteArray blockh,
+ jbyteArray raslineh,
+ jobject cmh)
+{
+ /* Patrick Naughton:
+ * Note that I ignore the possible existence of a local color map.
+ * I'm told there aren't many files around that use them, and the
+ * spec says it's defined for future use. This could lead to an
+ * error reading some files.
+ *
+ * Start reading the image data. First we get the intial code size
+ * and compute decompressor constant values, based on this code
+ * size.
+ *
+ * The GIF spec has it that the code size is the code size used to
+ * compute the above values is the code size given in the file,
+ * but the code size used in compression/decompression is the code
+ * size given in the file plus one. (thus the ++).
+ *
+ * Arthur van Hoff:
+ * The following narly code reads LZW compressed data blocks and
+ * dumps it into the image data. The input stream is broken up into
+ * blocks of 1-255 characters, each preceded by a length byte.
+ * 3-12 bit codes are read from these blocks. The codes correspond to
+ * entry is the hashtable (the prefix, suffix stuff), and the appropriate
+ * pixels are written to the image.
+ */
+ static int verbose = 0;
+
+ int clearCode = (1 << initCodeSize);
+ int eofCode = clearCode + 1;
+ int bitMask;
+ int curCode;
+ int outCount;
+
+ /* Variables used to form reading data */
+ int blockEnd = 0;
+ int remain = 0;
+ int byteoff = 0;
+ int accumbits = 0;
+ int accumdata = 0;
+
+ /* Variables used to decompress the data */
+ int codeSize = initCodeSize + 1;
+ int maxCode = 1 << codeSize;
+ int codeMask = maxCode - 1;
+ int freeCode = clearCode + 2;
+ int code = 0;
+ int oldCode = 0;
+ unsigned char prevChar = 0;
+
+ /* Temproray storage for decompression */
+ short *prefix;
+ unsigned char *suffix = NULL;
+ unsigned char *outCode = NULL;
+ unsigned char *rasline = NULL;
+ unsigned char *block = NULL;
+
+ jshortArray prefixh = (*env)->GetObjectField(env, this, prefixID);
+ jbyteArray suffixh = (*env)->GetObjectField(env, this, suffixID);
+ jbyteArray outCodeh = (*env)->GetObjectField(env, this, outCodeID);
+
+ int blockLength = 0;
+
+ /* Variables used for writing pixels */
+ int x = width;
+ int y = 0;
+ int off = 0;
+ int passinc = interlace ? 8 : 1;
+ int passht = passinc;
+ int len;
+
+ if (blockh == 0 || raslineh == 0
+ || prefixh == 0 || suffixh == 0
+ || outCodeh == 0)
+ {
+ JNU_ThrowNullPointerException(env, 0);
+ return 0;
+ }
+ if (((*env)->GetArrayLength(env, prefixh) != 4096) ||
+ ((*env)->GetArrayLength(env, suffixh) != 4096) ||
+ ((*env)->GetArrayLength(env, outCodeh) != OUTCODELENGTH))
+ {
+ JNU_ThrowArrayIndexOutOfBoundsException(env, 0);
+ return 0;
+ }
+
+ if (verbose) {
+ fprintf(stdout, "Decompressing...");
+ }
+
+ /* Fix for bugid 4216605 Some animated GIFs display corrupted. */
+ bitMask = clearCode - 1;
+
+ GET_ARRAYS();
+
+ /* Read codes until the eofCode is encountered */
+ for (;;) {
+ if (accumbits < codeSize) {
+ /* fill the buffer if needed */
+ while (remain < 2) {
+ if (blockEnd) {
+ /* Sometimes we have one last byte to process... */
+ if (remain == 1 && accumbits + 8 >= codeSize) {
+ remain--;
+ goto last_byte;
+ }
+ RELEASE_ARRAYS();
+ if (off > 0) {
+ (*env)->CallIntMethod(env, this, sendID,
+ relx, rely + y,
+ width, passht,
+ raslineh, cmh);
+ }
+ /* quietly accept truncated GIF images */
+ return 1;
+ }
+ /* move remaining bytes to the beginning of the buffer */
+ block[0] = block[byteoff];
+ byteoff = 0;
+
+ RELEASE_ARRAYS();
+ /* fill the block */
+ len = (*env)->CallIntMethod(env, this, readID,
+ blockh, remain, blockLength + 1);
+ if ((*env)->ExceptionOccurred(env)) {
+ return 0;
+ }
+ GET_ARRAYS();
+
+ remain += blockLength;
+ if (len > 0) {
+ remain -= (len - 1);
+ blockLength = 0;
+ } else {
+ blockLength = block[remain];
+ }
+ if (blockLength == 0) {
+ blockEnd = 1;
+ }
+ }
+ remain -= 2;
+
+ /* 2 bytes at a time saves checking for accumbits < codeSize.
+ * We know we'll get enough and also that we can't overflow
+ * since codeSize <= 12.
+ */
+ accumdata += (block[byteoff++] & 0xff) << accumbits;
+ accumbits += 8;
+ last_byte:
+ accumdata += (block[byteoff++] & 0xff) << accumbits;
+ accumbits += 8;
+ }
+
+ /* Compute the code */
+ code = accumdata & codeMask;
+ accumdata >>= codeSize;
+ accumbits -= codeSize;
+
+ /*
+ * Interpret the code
+ */
+ if (code == clearCode) {
+ /* Clear code sets everything back to its initial value, then
+ * reads the immediately subsequent code as uncompressed data.
+ */
+ if (verbose) {
+ fprintf(stdout, ".");
+ fflush(stdout);
+ }
+
+ /* Note that freeCode is one less than it is supposed to be,
+ * this is because it will be incremented next time round the loop
+ */
+ freeCode = clearCode + 1;
+ codeSize = initCodeSize + 1;
+ maxCode = 1 << codeSize;
+ codeMask = maxCode - 1;
+
+ /* Continue if we've NOT reached the end, some Gif images
+ * contain bogus codes after the last clear code.
+ */
+ if (y < height) {
+ continue;
+ }
+
+ /* pretend we've reached the end of the data */
+ code = eofCode;
+ }
+
+ if (code == eofCode) {
+ /* make sure we read the whole block of pixels. */
+ flushit:
+ while (!blockEnd) {
+ if (verbose) {
+ fprintf(stdout, "flushing %d bytes\n", blockLength);
+ }
+ RELEASE_ARRAYS();
+ if ((*env)->CallIntMethod(env, this, readID,
+ blockh, 0, blockLength + 1) != 0
+ || (*env)->ExceptionOccurred(env))
+ {
+ /* quietly accept truncated GIF images */
+ return (!(*env)->ExceptionOccurred(env));
+ }
+ GET_ARRAYS();
+ blockLength = block[blockLength];
+ blockEnd = (blockLength == 0);
+ }
+ RELEASE_ARRAYS();
+ return 1;
+ }
+
+ /* It must be data: save code in CurCode */
+ curCode = code;
+ outCount = OUTCODELENGTH;
+
+ /* If greater or equal to freeCode, not in the hash table
+ * yet; repeat the last character decoded
+ */
+ if (curCode >= freeCode) {
+ if (curCode > freeCode) {
+ /*
+ * if we get a code too far outside our range, it
+ * could case the parser to start traversing parts
+ * of our data structure that are out of range...
+ */
+ goto flushit;
+ }
+ curCode = oldCode;
+ outCode[--outCount] = prevChar;
+ }
+
+ /* Unless this code is raw data, pursue the chain pointed
+ * to by curCode through the hash table to its end; each
+ * code in the chain puts its associated output code on
+ * the output queue.
+ */
+ while (curCode > bitMask) {
+ outCode[--outCount] = suffix[curCode];
+ if (outCount == 0) {
+ /*
+ * In theory this should never happen since our
+ * prefix and suffix arrays are monotonically
+ * decreasing and so outCode will only be filled
+ * as much as those arrays, but I don't want to
+ * take that chance and the test is probably
+ * cheap compared to the read and write operations.
+ * If we ever do overflow the array, we will just
+ * flush the rest of the data and quietly accept
+ * the GIF as truncated here.
+ */
+ goto flushit;
+ }
+ curCode = prefix[curCode];
+ }
+
+ /* The last code in the chain is treated as raw data. */
+ prevChar = (unsigned char)curCode;
+ outCode[--outCount] = prevChar;
+
+ /* Now we put the data out to the Output routine. It's
+ * been stacked LIFO, so deal with it that way...
+ *
+ * Note that for some malformed images we have to skip
+ * current frame and continue with rest of data
+ * because we may have not enough info to interpret
+ * corrupted frame correctly.
+ * However, we can not skip frame without decoding it
+ * and therefore we have to continue looping through data
+ * but skip internal output loop.
+ *
+ * In particular this is is possible when
+ * width of the frame is set to zero. If
+ * global width (i.e. width of the logical screen)
+ * is zero too then zero-length scanline buffer
+ * is allocated in java code and we have no buffer to
+ * store decoded data in.
+ */
+ len = OUTCODELENGTH - outCount;
+ while ((width > 0) && (--len >= 0)) {
+ rasline[off++] = outCode[outCount++];
+
+ /* Update the X-coordinate, and if it overflows, update the
+ * Y-coordinate
+ */
+ if (--x == 0) {
+ /* If a non-interlaced picture, just increment y to the next
+ * scan line. If it's interlaced, deal with the interlace as
+ * described in the GIF spec. Put the decoded scan line out
+ * to the screen if we haven't gone past the bottom of it
+ */
+ int count;
+ RELEASE_ARRAYS();
+ count = (*env)->CallIntMethod(env, this, sendID,
+ relx, rely + y,
+ width, passht,
+ raslineh, cmh);
+ if (count <= 0 || (*env)->ExceptionOccurred(env)) {
+ /* Nobody is listening any more. */
+ if (verbose) {
+ fprintf(stdout, "Orphan gif decoder quitting\n");
+ }
+ return 0;
+ }
+ GET_ARRAYS();
+ x = width;
+ off = 0;
+ /* pass inc ht ystart */
+ /* 0 8 8 0 */
+ /* 1 8 4 4 */
+ /* 2 4 2 2 */
+ /* 3 2 1 1 */
+ y += passinc;
+ while (y >= height) {
+ passinc = passht;
+ passht >>= 1;
+ y = passht;
+ if (passht == 0) {
+ goto flushit;
+ }
+ }
+ }
+ }
+
+ /* Build the hash table on-the-fly. No table is stored in the file. */
+ prefix[freeCode] = (short)oldCode;
+ suffix[freeCode] = prevChar;
+ oldCode = code;
+
+ /* Point to the next slot in the table. If we exceed the
+ * maxCode, increment the code size unless
+ * it's already 12. If it is, do nothing: the next code
+ * decompressed better be CLEAR
+ */
+ if (++freeCode >= maxCode) {
+ if (codeSize < 12) {
+ codeSize++;
+ maxCode <<= 1;
+ codeMask = maxCode - 1;
+ } else {
+ /* Just in case */
+ freeCode = maxCode - 1;
+ }
+ }
+ }
+out_of_memory:
+ RELEASE_ARRAYS();
+ return 0;
+}