/*
* Copyright (c) 1995, 2006, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.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;
/* We have verified the initial code size on the java layer.
* Here we just check bounds for particular indexes. */
if (freeCode >= 4096 || maxCode >= 4096) {
return 0;
}
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;
}