Milán Major

netscape-revival

1 branch
Code

sun-java/runtime/gifdecoder.c

/*
 * @(#)gifdecoder.c	1.2 95/10/11 Jim Graham
 *
 * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

#include <stdio.h>
#ifdef XP_MAC
#include "j_awt_image_IndexColorModel.h"
#include "jdk_java_io_InputStream.h"
#else
#include "java_awt_image_IndexColorModel.h"
#include "java_io_InputStream.h"
#endif
#include "sun_awt_image_GifImageDecoder.h"

#if defined(XP_PC) && !defined(_WIN32)
#if !defined(stderr)
extern FILE *stderr;
#endif
#if !defined(stdout)
extern FILE *stdout;
#endif
#endif

#define OUTCODELENGTH 1025

long
sun_awt_image_GifImageDecoder_parseImage(Hsun_awt_image_GifImageDecoder *this,
					 long width, long height,
					 long interlace, long initCodeSize,
					 HArrayOfByte *blockh,
					 HArrayOfByte *raslineh)
{
    /* 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.
     */
#ifdef TRIMMED
    static int verbose = 0;
#endif

    int32_t clearCode = (1L << initCodeSize);
    int32_t eofCode = clearCode + 1;
    int32_t bitMask;
    int32_t curCode;
    int32_t outCount;

    /* Variables used to form reading data */
    int32_t blockEnd = 0;
    int32_t remain = 0;
    int32_t byteoff = 0;
    int32_t accumbits = 0;
    int32_t accumdata = 0;

    /* Variables used to decompress the data */
    int32_t codeSize = initCodeSize + 1;
    int32_t maxCode = 1L << codeSize;
    int32_t codeMask = maxCode - 1;
    int32_t freeCode = clearCode + 2;
    int32_t code = 0;
    int32_t oldCode = 0;
    unsigned char prevChar = 0;

    /* Temproray storage for decompression */
    short prefix[4096];
    unsigned char suffix[4096];
    unsigned char outCode[OUTCODELENGTH];
    unsigned char *rasline;
    unsigned char *block;
    int32_t blockLength;

    /* Variables used for writing pixels */
    int32_t x = width;
    int32_t y = 0;
    int32_t off = 0;
    int32_t pass = 0;
    int32_t len;

    ExecEnv *ee = EE();
    Hjava_io_InputStream *input;

    if (blockh == 0 || raslineh == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return 0;
    }
    input = unhand(this)->input;
    rasline = (unsigned char *) unhand(raslineh)->body;
    block = (unsigned char *) unhand(blockh)->body;
    bitMask = unhand(unhand(this)->model)->map_size - 1;

#ifndef TRIMMED
    if (verbose) {
	printf("Decompressing...");
	fflush(stdout);
    }
#endif /* TRIMMED */

    /* Read codes until the eofCode is encountered */
    for (;;) {
	if (accumbits < codeSize) {
	    /* fill the buffer if needed */
	    remain -= 2;
	    while (remain < 0 && !blockEnd) {
		/* move remaining bytes to the beginning of the buffer */
		block[0] = block[byteoff];
		byteoff = 0;

		/* read the next block length */
		blockLength = execute_java_dynamic_method(ee, (void *) input,
							  "read", "()I");
		if (exceptionOccurred(ee)) {
		    return 0;
		}
		if (blockLength < 0) {
		    /*	throw new IOException(); */
		    if (off > 0) {
			execute_java_dynamic_method(ee, (void *) this,
						    "sendPixels",
						    "(II[B)I",
						    y, width, raslineh);
			if (exceptionOccurred(ee)) {
			    return 0;
			}
		    }
		    /* quietly accept truncated GIF images */
		    return 1;
		}
		if (blockLength == 0) {
		    blockEnd = 1;
		}

		/* fill the block */
		if (!execute_java_dynamic_method(ee, (void *) this,
						 "readBytes", "([BII)Z",
						 blockh, remain + 2,
						 blockLength)
		    || exceptionOccurred(ee))
		{
		    /*	throw new IOException(); */
		    /* quietly accept truncated GIF images */
		    return 1;
		}
		remain += blockLength;
	    }

	    /* 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++] & 0xffL) << accumbits;
	    accumbits += 8;
	    accumdata += (block[byteoff++] & 0xffL) << 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.
	     */
#ifndef TRIMMED
	    if (verbose) {
		printf(".");
		fflush(stdout);
	    }
#endif /* TRIMMED */

	    /* 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 = 1L << 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. */
	    if (!blockEnd) {
		execute_java_dynamic_method(ee, (void *) input, "read", "()I");
	    }
	    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) {
	    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];
	     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...
	 */
	len = OUTCODELENGTH - outCount;
	while (--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
		 */
		int32_t count = execute_java_dynamic_method(ee, (void *) this,
							"sendPixels",
							"(II[B)I",
							y, width, raslineh);
		if (count <= 0 || exceptionOccurred(ee)) {
		    /* Nobody is listening any more. */
#ifndef TRIMMED
		    if (verbose) {
			printf("Orphan gif decoder quitting\n");
		    }
#endif /* TRIMMED */
		    return 0;
		}
		x = width;
		off = 0;
		if (interlace) {
		    switch (pass) {
		    case 0:
			y += 8;
			if (y >= height) {
			    pass++;
			    y = 4;
			}
			break;
		    case 1:
			y += 8;
			if (y >= height) {
			    pass++;
			    y = 2;
			}
			break;
		    case 2:
			y += 4;
			if (y >= height) {
			    pass++;
			    y = 1;
			}
			break;
		    case 3:
			y += 2;
			break;
		    }
		} else {
		    y++;
		}

		/* Some files overrun the end */
		if (y >= height) {
		    return 1;
		}
	    }
	}

	/* 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 <<= 1L;
		codeMask = maxCode - 1;
	    }
	}
    }
}