|
Java example source code file (gifdecoder.c)
The gifdecoder.c Java example source code/* * 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; } Other Java examples (source code examples)Here is a short list of links related to this Java gifdecoder.c source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.