/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/*
* This file contains the code to link the Java Image I/O JPEG plug-in
* to the IJG library used to read and write JPEG files. Much of it has
* been copied, updated, and annotated from the jpegdecoder.c AWT JPEG
* decoder. Where that code was unclear, the present author has either
* rewritten the relevant section or commented it for the sake of future
* maintainers.
*
* In particular, the way the AWT code handled progressive JPEGs seems
* to me to be only accidentally correct and somewhat inefficient. The
* scheme used here represents the way I think it should work. (REV 11/00)
*/
#include <stdlib.h>
#include <setjmp.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
/* java native interface headers */
#include "jni.h"
#include "jni_util.h"
/* headers from the JPEG library */
#include <jpeglib.h>
#include "jerror.h"
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/* Cached Java method ids */
/*
* Defined in jpegdecoder.c. Copy code from there if and
* when that disappears. */
/*
* The following sets of defines must match the warning messages in the
* Java code.
*/
/* Reader warnings */
#define READ_NO_EOI 0
/* Writer warnings */
/* Return codes for various ops */
#define NOT_OK 0
/*
* First we define two objects, one for the stream and buffer and one
* for pixels. Both contain references to Java objects and pointers to
* pinned arrays. These objects can be used for either input or
* output. Pixels can be accessed as either INT32s or bytes.
* Every I/O operation will have one of each these objects, one for
* the stream and the other to hold pixels, regardless of the I/O direction.
*/
/******************** StreamBuffer definition ************************/
typedef struct streamBufferStruct {
/*
* This buffer size was set to 64K in the old classes, 4K by default in the
* IJG library, with the comment "an efficiently freadable size", and 1K
* in AWT.
* Unlike in the other Java designs, these objects will persist, so 64K
* seems too big and 1K seems too small. If 4K was good enough for the
* IJG folks, it's good enough for me.
*/
/*
* Used to signal that no data need be restored from an unpin to a pin.
* I.e. the buffer is empty.
*/
// Forward reference
/*
* Initialize a freshly allocated StreamBuffer object. The stream is left
* null, as it will be set from Java by setSource, but the buffer object
* is created and a global reference kept. Returns OK on success, NOT_OK
* if allocating the buffer or getting a global reference for it failed.
*/
/* Initialize a new buffer */
if (hInputBuffer == NULL) {
"Initializing Reader");
return NOT_OK;
}
"Initializing Reader");
return NOT_OK;
}
return OK;
}
/*
* Free all resources associated with this streamBuffer. This must
* be called to dispose the object to avoid leaking global references, as
* resetStreamBuffer does not release the buffer reference.
*/
}
}
// Forward reference
/*
* Resets the state of a streamBuffer object that has been in use.
* The global reference to the stream is released, but the reference
* to the buffer is retained. The buffer is unpinned if it was pinned.
* All other state is reset.
*/
}
sb->remaining_skip = 0;
}
/*
* Pins the data buffer associated with this stream. Returns OK on
* success, NOT_OK on failure, as GetPrimitiveArrayCritical may fail.
*/
NULL);
return NOT_OK;
}
}
}
return OK;
}
/*
* Unpins the data buffer associated with this stream.
*/
} else {
}
0);
}
}
/*
* Clear out the streamBuffer. This just invalidates the data in the buffer.
*/
}
/*************************** end StreamBuffer definition *************/
/*************************** Pixel Buffer definition ******************/
typedef struct pixelBufferStruct {
unsigned int byteBufferLength;
union pixptr {
} buf;
/*
* Initialize a freshly allocated PixelBuffer. All fields are simply
* set to NULL, as we have no idea what size buffer we will need.
*/
pb->byteBufferLength = 0;
}
/*
* Set the pixelBuffer to use the given buffer, acquiring a new global
* reference for it. Returns OK on success, NOT_OK on failure.
*/
"Setting Pixel Buffer");
return NOT_OK;
}
return OK;
}
// Forward reference
/*
* Resets a pixel buffer to its initial state. Unpins any pixel buffer,
* releases the global reference, and resets fields to NULL. Use this
* method to dispose the object as well (there is no destroyPixelBuffer).
*/
pb->byteBufferLength = 0;
}
}
/*
* Pins the data buffer. Returns OK on success, NOT_OK on failure.
*/
return NOT_OK;
}
}
return OK;
}
/*
* Unpins the data buffer.
*/
0);
}
}
/********************* end PixelBuffer definition *******************/
/********************* ImageIOData definition ***********************/
/* The number of possible incoming values to be scaled. */
/*
* The principal imageioData object, opaque to I/O direction.
* Each JPEGImageReader will have associated with it a
* jpeg_decompress_struct, and similarly each JPEGImageWriter will
* have associated with it a jpeg_compress_struct. In order to
* ensure that these associations persist from one native call to
* the next, and to provide a central locus of imageio-specific
* data, we define an imageioData struct containing references
* to the Java object and the IJG structs. The functions
* that manipulate these objects know whether input or output is being
* performed and therefore know how to manipulate the contents correctly.
* If for some reason they don't, the direction can be determined by
* checking the is_decompressor field of the jpegObj.
* In order for lower level code to determine a
* Java object given an IJG struct, such as for dispatching warnings,
* we use the client_data field of the jpeg object to store a pointer
* to the imageIOData object. Maintenance of this pointer is performed
* exclusively within the following access functions. If you
* change that, you run the risk of dangling pointers.
*/
typedef struct imageIODataStruct {
/*
* Allocate and initialize a new imageIOData object to associate the
* jpeg object and the Java object. Returns a pointer to the new object
* on success, NULL on failure.
*/
return NULL;
}
#ifdef DEBUG_IIO_JPEG
#endif
return NULL;
}
return NULL;
}
return data;
}
/*
* Resets the imageIOData object to its initial state, as though
* it had just been allocated and initialized.
*/
}
/*
* Releases all resources held by this object and its subobjects,
* frees the object, and returns the jpeg object. This method must
* be called to avoid leaking global references.
* Note that the jpeg object is not freed or destroyed, as that is
* the client's responsibility, although the client_data field is
* cleared.
*/
return ret;
}
/******************** end ImageIOData definition ***********************/
/******************** Java array pinning and unpinning *****************/
/* We use Get/ReleasePrimitiveArrayCritical functions to avoid
* the need to copy array elements for the above two objects.
*
* 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!
*/
/*
* Release (unpin) all the arrays in use during a read.
*/
{
}
/*
* Get (pin) all the arrays in use during a read.
*/
return NOT_OK;
}
return NOT_OK;
}
return OK;
}
/****** end of Java array pinning and unpinning ***********/
/****** Error Handling *******/
/*
* setup, as both the AWT jpeg decoder and the com.sun... JPEG classes
* setup thier own. Ultimately these should be integrated, as they all
* do pretty much the same thing.
*/
struct sun_jpeg_error_mgr {
};
/*
* Here's the routine that will replace the standard error_exit method:
*/
METHODDEF(void)
{
/* cinfo->err really points to a sun_jpeg_error_mgr struct */
/* For Java, we will format the message and put it in the error we throw. */
/* Return control to the setjmp point */
}
/*
* Error Message handling
*
* This overrides the output_message method to send JPEG messages
*
*/
METHODDEF(void)
{
/* Create the message */
// Create a new java string from the message
if (cinfo->is_decompressor) {
string);
} else {
string);
}
}
/* End of verbatim copy from jpegdecoder.c */
/*************** end of error handling *********************/
/*************** Shared utility code ***********************/
/* Now we need a new global reference for the stream */
"Setting Stream");
return;
}
}
/* And finally reset state */
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error
while aborting. */
buffer);
}
return;
}
}
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error
while aborting. */
}
return;
}
}
if (info->is_decompressor) {
} else {
}
}
}
}
int i, j;
#ifdef DEBUG_IIO_JPEG
#endif
if (qlen > NUM_QUANT_TBLS) {
/* Ignore extra qunterization tables. */
}
for (i = 0; i < qlen; i++) {
if (cinfo->is_decompressor) {
decomp->quant_tbl_ptrs[i] =
}
} else {
comp->quant_tbl_ptrs[i] =
}
}
for (j = 0; j < 64; j++) {
}
0);
}
return qlen;
}
int i;
// lengths
NULL);
if (hlensLen > 16) {
/* Ignore extra elements of bits array. Only 16 elements can be
stored. 0-th element is not used. (see jpeglib.h, line 107) */
hlensLen = 16;
}
for (i = 1; i <= hlensLen; i++) {
}
// values
NULL);
if (hvalsLen > 256) {
/* Ignore extra elements of hufval array. Only 256 elements
can be stored. (see jpeglib.h, line 109) */
hlensLen = 256;
}
for (i = 0; i < hvalsLen; i++) {
}
}
int i;
if (hlen > NUM_HUFF_TBLS) {
/* Ignore extra DC huffman tables. */
}
for (i = 0; i < hlen; i++) {
if (cinfo->is_decompressor) {
decomp->dc_huff_tbl_ptrs[i] =
}
} else {
comp->dc_huff_tbl_ptrs[i] =
}
}
}
if (hlen > NUM_HUFF_TBLS) {
/* Ignore extra AC huffman tables. */
}
for (i = 0; i < hlen; i++) {
if (cinfo->is_decompressor) {
decomp->ac_huff_tbl_ptrs[i] =
}
} else {
comp->ac_huff_tbl_ptrs[i] =
}
}
}
return hlen;
}
/*************** end of shared utility code ****************/
/********************** Reader Support **************************/
/********************** Source Management ***********************/
/*
* INPUT HANDLING:
*
* The JPEG library's input management is defined by the jpeg_source_mgr
* structure which contains two fields to convey the information in the
* buffer and 5 methods which perform all buffer management. The library
* defines a standard input manager that uses stdio for obtaining compressed
* jpeg data, but here we need to use Java to get our data.
*
* We use the library jpeg_source_mgr but our own routines that access
* imageio-specific information in the imageIOData structure.
*/
/*
* Initialize source. This is called by jpeg_read_header() before any
* data is actually read. Unlike init_destination(), it may leave
* bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
* will occur immediately).
*/
GLOBAL(void)
{
src->bytes_in_buffer = 0;
}
/*
* This is called whenever bytes_in_buffer has reached zero and more
* data is wanted. In typical applications, it should read fresh data
* into the buffer (ignoring the current state of next_input_byte and
* bytes_in_buffer), reset the pointer & count to the start of the
* buffer, and return TRUE indicating that the buffer has been reloaded.
* It is not necessary to fill the buffer entirely, only to obtain at
* least one more byte. bytes_in_buffer MUST be set to a positive value
* if TRUE is returned. A FALSE return should only be used when I/O
* suspension is desired (this mode is discussed in the next section).
*/
/*
* Note that with I/O suspension turned on, this procedure should not
* do any work since the JPEG library has a very simple backtracking
* mechanism which relies on the fact that the buffer will be filled
* only when it has backed out to the top application level. When
* suspendable is turned on, imageio_fill_suspended_buffer will
* do the actual work of filling the buffer.
*/
{
int ret;
/* This is where input suspends */
if (sb->suspendable) {
return FALSE;
}
#ifdef DEBUG_IIO_JPEG
printf("Filling input buffer, remaining skip is %ld, ",
sb->remaining_skip);
#endif
/*
* Definitively skips. Could be left over if we tried to skip
* more than a buffer's worth but suspended when getting the next
* buffer. Now we aren't suspended, so we can catch up.
*/
if (sb->remaining_skip) {
}
/*
* Now fill a complete buffer, or as much of one as the stream
* will give us if we are near the end.
*/
sb->hstreamBuffer, 0,
sb->bufferLength);
}
#ifdef DEBUG_IIO_JPEG
#endif
/*
* If we have reached the end of the stream, then the EOI marker
* is missing. We accept such streams but generate a warning.
* The image is likely to be corrupted, though everything through
* the end of the last complete MCU should be usable.
*/
if (ret <= 0) {
#ifdef DEBUG_IIO_JPEG
#endif
}
ret = 2;
}
return TRUE;
}
/*
* With I/O suspension turned on, the JPEG library requires that all
* buffer filling be done at the top application level, using this
* function. Due to the way that backtracking works, this procedure
* saves all of the data that was left in the buffer when suspension
* occured and read new data only at the end.
*/
GLOBAL(void)
{
/*
* The original (jpegdecoder.c) had code here that called
* InputStream.available and just returned if the number of bytes
* available was less than any remaining skip. Presumably this was
* to avoid blocking, although the benefit was unclear, as no more
* decompression can take place until more data is available, so
* the code would block on input a little further along anyway.
* ImageInputStreams don't have an available method, so we'll just
* block in the skip if we have to.
*/
if (sb->remaining_skip) {
}
/* Save the data currently in the buffer */
}
if (buflen <= 0) {
}
return;
}
}
/*
* If we have reached the end of the stream, then the EOI marker
* is missing. We accept such streams but generate a warning.
* The image is likely to be corrupted, though everything through
* the end of the last complete MCU should be usable.
*/
if (ret <= 0) {
}
ret = 2;
}
return;
}
/*
* Skip num_bytes worth of data. The buffer pointer and count are
* advanced over num_bytes input bytes, using the input stream
* skipBytes method if the skip is greater than the number of bytes
* in the buffer. This is used to skip over a potentially large amount of
* uninteresting data (such as an APPn marker). bytes_in_buffer will be
* zero on return if the skip is larger than the current contents of the
* buffer.
*
* A negative skip count is treated as a no-op. A zero skip count
* skips any remaining skip from a previous skip while suspended.
*
* Note that with I/O suspension turned on, this procedure does not
* call skipBytes since the JPEG library has a very simple backtracking
* mechanism which relies on the fact that the application level has
* exclusive control over actual I/O.
*/
GLOBAL(void)
{
if (num_bytes < 0) {
return;
}
sb->remaining_skip = 0;
/* First the easy case where we are skipping <= the current contents. */
return;
}
/*
* We are skipping more than is in the buffer. We empty the buffer and,
* if we aren't suspended, call the Java skipBytes method. We always
* leave the buffer empty, to be filled by either fill method above.
*/
src->bytes_in_buffer = 0;
if (sb->suspendable) {
return;
}
}
/*
* If we have reached the end of the stream, then the EOI marker
* is missing. We accept such streams but generate a warning.
* The image is likely to be corrupted, though everything through
* the end of the last complete MCU should be usable.
*/
if (ret <= 0) {
}
}
}
/*
* Terminate source --- called by jpeg_finish_decompress() after all
* data for an image has been read. In our case pushes back any
* remaining data, as it will be for another image and must be available
* for java to find out that there is another image. Also called if
* reseting state after reading a tables-only image.
*/
GLOBAL(void)
{
// To pushback, just seek back by src->bytes_in_buffer
if (src->bytes_in_buffer > 0) {
}
src->bytes_in_buffer = 0;
//src->next_input_byte = sb->buf;
}
}
/********************* end of source manager ******************/
/********************* ICC profile support ********************/
/*
* The following routines are modified versions of the ICC
* profile support routines available from the IJG website.
* The originals were written by Todd Newman
* <tdn@eccentric.esd.sgi.com> and modified by Tom Lane for
* the IJG. They are further modified to fit in the context
* of the imageio JPEG plug-in.
*/
/*
* Since an ICC profile can be larger than the maximum size of a JPEG marker
* (64K), we need provisions to split it into multiple markers. The format
* defined by the ICC specifies one or more APP2 markers containing the
* following data:
* Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
* Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
* Number of markers Total number of APP2's used (1 byte)
* Profile data (remainder of APP2 data)
* Decoders should use the marker sequence numbers to reassemble the profile,
* rather than assuming that the APP2 markers appear in the correct sequence.
*/
/*
* Handy subroutine to test whether a saved marker is an ICC profile marker.
*/
static boolean
{
return
/* verify the identifying string */
}
/*
* See if there was an ICC profile in the JPEG file being read;
* if so, reassemble and return the profile data as a new Java byte array.
* If there was no ICC profile, return NULL.
*
* If the file contains invalid ICC APP2 markers, we throw an IIOException
* with an appropriate message.
*/
{
int num_markers = 0;
int num_found_markers = 0;
int seq_no;
unsigned int total_length;
/* This first pass over the saved markers discovers whether there are
* any ICC markers and verifies the consistency of the marker numbering.
*/
if (marker_is_icc(marker)) {
if (num_markers == 0)
"Invalid icc profile: inconsistent num_markers fields");
return NULL;
}
/* Some third-party tools produce images with profile chunk
* numeration started from zero. It is inconsistent with ICC
* spec, but seems to be recognized by majority of image
* processing tools, so we should be more tolerant to this
* departure from the spec.
*/
"Invalid icc profile: bad sequence number");
return NULL;
}
"Invalid icc profile: duplicate sequence numbers");
return NULL;
}
}
}
if (num_markers == 0)
return NULL; // There is no profile
if (num_markers != num_found_markers) {
"Invalid icc profile: invalid number of icc markers");
return NULL;
}
/* Check for missing markers, count total space needed.
*/
total_length = 0;
unsigned int length;
"Invalid icc profile: missing sequence number");
return NULL;
}
/* check the data length correctness */
"Invalid icc profile: invalid data length");
return NULL;
}
}
if (total_length <= 0) {
"Invalid icc profile: found only empty markers");
return NULL;
}
/* Allocate a Java byte array for assembled data */
"Reading ICC profile");
return NULL;
}
data,
NULL);
"Unable to pin icc profile data array");
return NULL;
}
/* and fill it in */
unsigned int length =
}
/* finally, unpin the array */
data,
0);
return data;
}
/********************* end of ICC profile support *************/
/********************* Reader JNI calls ***********************/
cls,
"readInputData",
"([BII)I");
cls,
"skipInputBytes",
"(J)J");
cls,
"warningOccurred",
"(I)V");
cls,
"warningWithMessage",
cls,
"setImageData",
"(IIIII[B)V");
cls,
"acceptPixels",
"(IZ)V");
cls,
"passStarted",
"(I)V");
cls,
"passComplete",
"()V");
cls,
"pushBack",
"(I)V");
"qTable",
"[I");
"lengths",
"[S");
"values",
"[S");
}
/* This struct contains the JPEG decompression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
malloc(sizeof(struct jpeg_decompress_struct));
"Initializing Reader");
return 0;
}
/* We use our private extension JPEG error handler.
*/
"Initializing Reader");
return 0;
}
/* We set up the normal JPEG error routines, then override error_exit. */
/* We need to setup our own print routines */
/* Now we can setjmp before every call to the library */
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error. */
buffer);
return 0;
}
/* Perform library initialization */
// Set up to keep any APP2 markers, as these might contain ICC profile
// data
/*
* Now set up our source.
*/
"Initializing Reader");
return 0;
}
/* set up the association to persist for future calls */
"Initializing Reader");
return 0;
}
return ptr_to_jlong(ret);
}
/*
* When we set a source from Java, we set up the stream in the streamBuf
* object. If there was an old one, it is released first.
*/
"Attempting to use reader after dispose()");
return;
}
}
/*
* For EXIF images, the APP1 will appear immediately after the SOI,
* so it's safe to only look at the first marker in the list.
* (see http://www.exif.org/Exif2-2.PDF, section 4.7, page 58)
*/
#define IS_EXIF(c) \
int ret;
"Attempting to use reader after dispose()");
return JNI_FALSE;
}
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error
while reading the header. */
buffer);
}
return retval;
}
#ifdef DEBUG_IIO_JPEG
#endif
"Array pin failed");
return retval;
}
/*
* Now clear the input buffer if the Java code has done a seek
* on the stream since the last call, invalidating any buffer contents.
*/
if (clearFirst) {
src->bytes_in_buffer = 0;
}
if (ret == JPEG_HEADER_TABLES_ONLY) {
#ifdef DEBUG_IIO_JPEG
printf("just read tables-only image; q table 0 at %p\n",
cinfo->quant_tbl_ptrs[0]);
#endif
} else {
/*
* Now adjust the jpeg_color_space variable, which was set in
* default_decompress_parms, to reflect our differences from IJG
*/
switch (cinfo->jpeg_color_space) {
default :
break;
case JCS_YCbCr:
/*
* There are several possibilities:
* - we got image with embeded colorspace
* Use it. User knows what he is doing.
* - we got JFIF image
* Must be YCbCr (see http://www.w3.org/Graphics/JPEG/jfif3.pdf, page 2)
* - we got EXIF image
* Must be YCbCr (see http://www.exif.org/Exif2-2.PDF, section 4.7, page 63)
* - something else
* Apply heuristical rules to identify actual colorspace.
*/
if (cinfo->saw_Adobe_marker) {
/*
* IJG guesses this is YCbCr and emits a warning
* We would rather not guess. Then the user knows
* To read this as a Raster if at all
*/
}
/*
* IJG assumes all unidentified 3-channels are YCbCr.
* We assume that only if the second two channels are
* subsampled (either horizontally or vertically). If not,
* we assume RGB.
*
* 4776576: Some digital cameras output YCbCr JPEG images
* that do not contain a JFIF APP0 marker but are only
* vertically subsampled (no horizontal subsampling).
* We should only assume this is RGB data if the subsampling
* factors for the second two channels are the same as the
* first (check both horizontal and vertical factors).
*/
{
/* output is already RGB, so it stays the same */
}
}
break;
#ifdef YCCALPHA
case JCS_YCC:
break;
#endif
case JCS_YCCK:
/*
* IJG guesses this is YCCK and emits a warning
* We would rather not guess. Then the user knows
* To read this as a Raster if at all
*/
}
break;
case JCS_CMYK:
/*
* IJG assumes all unidentified 4-channels are CMYK.
* We assume that only if the second two channels are
* not subsampled (either horizontally or vertically).
* If they are, we assume YCCK.
*/
{
/* Leave the output space as CMYK */
}
}
/* read icc profile data */
return retval;
}
if (reset) {
}
}
return retval;
}
"Attempting to use reader after dispose()");
return;
}
}
int i;
int scanlineLimit;
int pixelStride;
int targetLine;
/* verify the inputs */
"Attempting to use reader after dispose()");
return JNI_FALSE;
}
return JNI_FALSE;
}
(minProgressivePass < 0) ||
{
"Invalid argument to native readImage");
return JNI_FALSE;
}
}
}
/*
* First get the source bands array and copy it to our local array
* so we don't have to worry about pinning and unpinning it again.
*/
"Initializing Read");
return JNI_FALSE;
}
for (i = 0; i < numBands; i++) {
if (orderedBands && (bands[i] != i)) {
}
}
#ifdef DEBUG_IIO_JPEG
printf("---- in reader.read ----\n");
printf("bands array: ");
for (i = 0; i < numBands; i++) {
}
printf("\n");
printf("jq table 0 at %p\n",
cinfo->quant_tbl_ptrs[0]);
#endif
/* Set the buffer as our PixelBuffer */
}
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error
while reading. */
buffer);
}
if (scanLinePtr != NULL) {
scanLinePtr = NULL;
}
}
"Array pin failed");
}
// If there are no tables in our structure and table arguments aren't
// NULL, use the table arguments.
}
TRUE);
}
if (progressive) {
if (maxProgressivePass < MAX_JAVA_INT) {
maxProgressivePass++; // For testing
}
}
"Invalid argument to native readImage");
}
if (cinfo->output_components <= 0 ||
{
"Invalid number of output components");
}
// Allocate a 1-scanline buffer
if (scanLinePtr == NULL) {
"Reading JPEG Stream");
}
// loop over progressive passes
while (!done) {
if (progressive) {
// initialize the next pass. Note that this skips up to
// the first interesting pass.
if (wantUpdates) {
}
} else if (wantUpdates) {
0);
}
// Skip until the first interesting line
}
targetLine = 0;
// Now mangle it into our buffer
// Optimization: The component bands are ordered sequentially,
// so we can simply use memcpy() to copy the intermediate
// scanline buffer into the raster.
if (pixelLimit > in) {
}
}
} else {
in < pixelLimit &&
in += pixelStride) {
for (i = 0; i < numBands; i++) {
}
}
}
// And call it back to Java
this,
targetLine++,
}
// And skip over uninteresting lines to the next subsampled line
// Ensure we don't go past the end of the image
// Lines to skip based on subsampling
// Lines left in the image
// Take the minimum
}
for(i = 0; i < skipLines; i++) {
}
}
if (progressive) {
// Call Java to notify pass complete
if (jpeg_input_complete(cinfo)
}
} else {
}
if (wantUpdates) {
}
}
/*
* We are done, but we might not have read all the lines, or all
* the passes, so use jpeg_abort instead of jpeg_finish_decompress.
*/
// if ((cinfo->output_scanline == cinfo->output_height) &&
//(jpeg_input_complete(cinfo))) { // We read the whole file
} else {
}
}
"Attempting to use reader after dispose()");
return;
}
}
"Attempting to use reader after dispose()");
return;
}
}
"Attempting to use reader after dispose()");
return;
}
/*
* The tables have not been reset, and there is no way to do so
* in IJG without leaking memory. The only situation in which
* this will cause a problem is if an image-only stream is read
* with this object without initializing the correct tables first.
* This situation, which should cause an error, might work but
* produce garbage instead. If the huffman tables are wrong,
* it will fail during the decode. If the q tables are wrong, it
* will look strange. This is very unlikely, so don't worry about
* it. To be really robust, we would keep a flag for table state
* and consult it to catch exceptional situations.
*/
/* above does not clean up the source, so we have to */
/*
We need to explicitly initialize exception handler or we may
longjump to random address from the term_source()
*/
/*
We may get IOException from pushBack() here.
However it could be legal if original input stream was closed
earlier (for example because network connection was closed).
Unfortunately, image inputstream API has no way to check whether
stream is already closed or IOException was thrown because of some
other IO problem,
And we can not avoid call to pushBack() on closed stream for the
same reason.
So, for now we will silently eat this exception.
NB: this may be changed in future when ImageInputStream API will
become more flexible.
*/
}
} else {
}
}
}
/********************** end of Reader *************************/
/********************** Writer Support ************************/
/********************** Destination Manager *******************/
METHODDEF(void)
/*
* Initialize destination --- called by jpeg_start_compress
* before any data is actually written. The data arrays
* must be pinned before this is called.
*/
{
// We forgot to pin the array
}
}
/*
* Empty the output buffer --- called whenever buffer fills up.
*
* This routine writes the entire output buffer
* (ignoring the current state of next_output_byte & free_in_buffer),
* resets the pointer & count to the start of the buffer, and returns TRUE
* indicating that the buffer has been dumped.
*/
{
0,
sb->bufferLength);
}
return TRUE;
}
/*
* After all of the data has been encoded there may still be some
* more left over in some of the working buffers. Now is the
* time to clear them out.
*/
METHODDEF(void)
{
/* find out how much needs to be written */
/* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */
if (datacount != 0) {
0,
}
}
dest->free_in_buffer = 0;
}
/*
* Flush the destination buffer. This is not called by the library,
* but by our code below. This is the simplest implementation, though
* certainly not the most efficient.
*/
METHODDEF(void)
{
}
/********************** end of destination manager ************/
/********************** Writer JNI calls **********************/
cls,
"writeOutputData",
"([BII)V");
cls,
"warningOccurred",
"(I)V");
cls,
"warningWithMessage",
cls,
"writeMetadata",
"()V");
cls,
"grabPixels",
"(I)V");
"qTable",
"[I");
"lengths",
"[S");
"values",
"[S");
}
/* This struct contains the JPEG compression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
malloc(sizeof(struct jpeg_compress_struct));
"Initializing Writer");
return 0;
}
/* We use our private extension JPEG error handler.
*/
"Initializing Writer");
return 0;
}
/* We set up the normal JPEG error routines, then override error_exit. */
/* We need to setup our own print routines */
/* Now we can setjmp before every call to the library */
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error. */
buffer);
return 0;
}
/* Perform library initialization */
/* Now set up the destination */
"Initializing Writer");
return 0;
}
dest->free_in_buffer = 0;
/* set up the association to persist for future calls */
"Initializing Writer");
return 0;
}
return ptr_to_jlong(ret);
}
"Attempting to use writer after dispose()");
return;
}
// Don't call the init method, as that depends on pinned arrays
}
"Attempting to use writer after dispose()");
return;
}
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error
while writing. */
buffer);
}
return;
}
"Array pin failed");
return;
}
#ifdef DEBUG_IIO_JPEG
printf("in writeTables: qtables not NULL\n");
#endif
}
if (DCHuffmanTables != NULL) {
}
}
int i, j;
int pixelStride;
int targetLine;
int *scanptr;
/* verify the inputs */
"Attempting to use writer after dispose()");
return JNI_FALSE;
}
// H tables can be null if optimizing
(componentIds == NULL) ||
(QtableSelectors == NULL) ||
return JNI_FALSE;
}
(srcWidth < 0) ||
(destHeight < 0) ||
{
"Invalid argument to native writeImage");
return JNI_FALSE;
}
}
for (i = 0; i < numBands; i++) {
if (bandSize[i] != JPEG_BAND_SIZE) {
"Writing JPEG Stream");
return JNI_FALSE;
}
}
"Writing JPEG Stream");
return JNI_FALSE;
}
for (j = 0; j <= maxBandValue; j++) {
}
}
}
/* Set the buffer as our PixelBuffer */
}
// Allocate a 1-scanline buffer
if (scanLinePtr == NULL) {
"Writing JPEG Stream");
}
/* Establish the setjmp return context for sun_jpeg_error_exit to use. */
/* If we get here, the JPEG code has signaled an error
while writing. */
buffer);
}
for (i = 0; i < numBands; i++) {
}
}
}
}
// set up parameters
// copy componentIds
"Writing JPEG");
return JNI_FALSE;
}
for (i = 0; i < numBands; i++) {
}
if (!optimize) {
// Set the h tables
writeDHT);
}
"Array pin failed");
}
if (progressive) {
if (numScans == 0) { // then use default scans
} else {
// Copy the scanInfo to a local array
// The following is copied from jpeg_simple_progression:
/* Allocate space for script.
* We need to put it in the permanent pool in case the application performs
* multiple compressions without changing the settings. To avoid a memory
* leak if jpeg_simple_progression is called repeatedly for the same JPEG
* object, we try to re-use previously allocated space, and we allocate
* enough space to handle YCbCr even if initially asked for grayscale.
*/
* sizeof(jpeg_scan_info));
}
// number of jints per scan is 9
// We avoid a memcpy to handle different size ints
for (i = 0; i < numScans*9; i++) {
}
}
}
#ifdef DEBUG_IIO_JPEG
printf("writer setup complete, starting compressor\n");
#endif
// start the compressor; tables must already be set
if (haveMetadata) {
// Flush the buffer
// Call Java to write the metadata
this,
}
}
targetLine = 0;
// for each line in destHeight
// get the line from Java
this,
}
// subsample it into our buffer
out = scanLinePtr;
for (i = 0; i < numBands; i++) {
#ifdef DEBUG_IIO_JPEG
}
#endif
#ifdef DEBUG_IIO_JPEG
printf("\n");
}
#endif
} else {
}
}
}
// write it out
targetLine += stepY;
}
/*
* We are done, but we might not have done all the lines,
* so use jpeg_abort instead of jpeg_finish_compress.
*/
} else {
}
for (i = 0; i < numBands; i++) {
}
}
}
}
"Attempting to use writer after dispose()");
return;
}
}
"Attempting to use writer after dispose()");
return;
}
/*
* The tables have not been reset, and there is no way to do so
* in IJG without leaking memory. The only situation in which
* this will cause a problem is if an image-only stream is written
* with this object without initializing the correct tables first,
* which should not be possible.
*/
}
}