Path: blob/master/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
41152 views
/*1* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26* This file contains the code to link the Java Image I/O JPEG plug-in27* to the IJG library used to read and write JPEG files. Much of it has28* been copied, updated, and annotated from the jpegdecoder.c AWT JPEG29* decoder. Where that code was unclear, the present author has either30* rewritten the relevant section or commented it for the sake of future31* maintainers.32*33* In particular, the way the AWT code handled progressive JPEGs seems34* to me to be only accidentally correct and somewhat inefficient. The35* scheme used here represents the way I think it should work. (REV 11/00)36*/3738#include <stdlib.h>39#include <setjmp.h>40#include <assert.h>41#include <string.h>42#include <limits.h>4344/* java native interface headers */45#include "jni.h"46#include "jni_util.h"4748#include "com_sun_imageio_plugins_jpeg_JPEGImageReader.h"49#include "com_sun_imageio_plugins_jpeg_JPEGImageWriter.h"5051/* headers from the JPEG library */52#include <jpeglib.h>53#include <jerror.h>5455#undef MAX56#define MAX(a,b) ((a) > (b) ? (a) : (b))5758#ifdef __APPLE__59/* use setjmp/longjmp versions that do not save/restore the signal mask */60#define setjmp _setjmp61#define longjmp _longjmp62#endif6364/* Cached Java method ids */65static jmethodID JPEGImageReader_readInputDataID;66static jmethodID JPEGImageReader_skipInputBytesID;67static jmethodID JPEGImageReader_warningOccurredID;68static jmethodID JPEGImageReader_warningWithMessageID;69static jmethodID JPEGImageReader_setImageDataID;70static jmethodID JPEGImageReader_acceptPixelsID;71static jmethodID JPEGImageReader_pushBackID;72static jmethodID JPEGImageReader_passStartedID;73static jmethodID JPEGImageReader_passCompleteID;74static jmethodID JPEGImageReader_skipPastImageID;75static jmethodID JPEGImageWriter_writeOutputDataID;76static jmethodID JPEGImageWriter_warningOccurredID;77static jmethodID JPEGImageWriter_warningWithMessageID;78static jmethodID JPEGImageWriter_writeMetadataID;79static jmethodID JPEGImageWriter_grabPixelsID;80static jfieldID JPEGQTable_tableID;81static jfieldID JPEGHuffmanTable_lengthsID;82static jfieldID JPEGHuffmanTable_valuesID;8384/*85* Defined in jpegdecoder.c. Copy code from there if and86* when that disappears. */87extern JavaVM *the_jvm;8889/*90* The following sets of defines must match the warning messages in the91* Java code.92*/9394/* Reader warnings */95#define READ_NO_EOI 09697/* Writer warnings */9899/* Return codes for various ops */100#define OK 1101#define NOT_OK 0102103/*104* First we define two objects, one for the stream and buffer and one105* for pixels. Both contain references to Java objects and pointers to106* pinned arrays. These objects can be used for either input or107* output. Pixels can be accessed as either INT32s or bytes.108* Every I/O operation will have one of each these objects, one for109* the stream and the other to hold pixels, regardless of the I/O direction.110*/111112/******************** StreamBuffer definition ************************/113114typedef struct streamBufferStruct {115jweak ioRef; // weak reference to a provider of I/O routines116jbyteArray hstreamBuffer; // Handle to a Java buffer for the stream117JOCTET *buf; // Pinned buffer pointer */118size_t bufferOffset; // holds offset between unpin and the next pin119size_t bufferLength; // Allocated, nut just used120int suspendable; // Set to true to suspend input121long remaining_skip; // Used only on input122} streamBuffer, *streamBufferPtr;123124/*125* This buffer size was set to 64K in the old classes, 4K by default in the126* IJG library, with the comment "an efficiently freadable size", and 1K127* in AWT.128* Unlike in the other Java designs, these objects will persist, so 64K129* seems too big and 1K seems too small. If 4K was good enough for the130* IJG folks, it's good enough for me.131*/132#define STREAMBUF_SIZE 4096133134#define GET_IO_REF(io_name) \135do { \136if ((*env)->IsSameObject(env, sb->ioRef, NULL) || \137((io_name) = (*env)->NewLocalRef(env, sb->ioRef)) == NULL) \138{ \139cinfo->err->error_exit((j_common_ptr) cinfo); \140} \141} while (0) \142143/*144* Used to signal that no data need be restored from an unpin to a pin.145* I.e. the buffer is empty.146*/147#define NO_DATA ((size_t)-1)148149// Forward reference150static void resetStreamBuffer(JNIEnv *env, streamBufferPtr sb);151152/*153* Initialize a freshly allocated StreamBuffer object. The stream is left154* null, as it will be set from Java by setSource, but the buffer object155* is created and a global reference kept. Returns OK on success, NOT_OK156* if allocating the buffer or getting a global reference for it failed.157*/158static int initStreamBuffer(JNIEnv *env, streamBufferPtr sb) {159/* Initialize a new buffer */160jbyteArray hInputBuffer = (*env)->NewByteArray(env, STREAMBUF_SIZE);161if (hInputBuffer == NULL) {162(*env)->ExceptionClear(env);163JNU_ThrowByName( env,164"java/lang/OutOfMemoryError",165"Initializing Reader");166return NOT_OK;167}168sb->bufferLength = (*env)->GetArrayLength(env, hInputBuffer);169sb->hstreamBuffer = (*env)->NewGlobalRef(env, hInputBuffer);170if (sb->hstreamBuffer == NULL) {171JNU_ThrowByName( env,172"java/lang/OutOfMemoryError",173"Initializing Reader");174return NOT_OK;175}176177178sb->ioRef = NULL;179180sb->buf = NULL;181182resetStreamBuffer(env, sb);183184return OK;185}186187/*188* Free all resources associated with this streamBuffer. This must189* be called to dispose the object to avoid leaking global references, as190* resetStreamBuffer does not release the buffer reference.191*/192static void destroyStreamBuffer(JNIEnv *env, streamBufferPtr sb) {193resetStreamBuffer(env, sb);194if (sb->hstreamBuffer != NULL) {195(*env)->DeleteGlobalRef(env, sb->hstreamBuffer);196}197}198199// Forward reference200static void unpinStreamBuffer(JNIEnv *env,201streamBufferPtr sb,202const JOCTET *next_byte);203/*204* Resets the state of a streamBuffer object that has been in use.205* The global reference to the stream is released, but the reference206* to the buffer is retained. The buffer is unpinned if it was pinned.207* All other state is reset.208*/209static void resetStreamBuffer(JNIEnv *env, streamBufferPtr sb) {210if (sb->ioRef != NULL) {211(*env)->DeleteWeakGlobalRef(env, sb->ioRef);212sb->ioRef = NULL;213}214unpinStreamBuffer(env, sb, NULL);215sb->bufferOffset = NO_DATA;216sb->suspendable = FALSE;217sb->remaining_skip = 0;218}219220/*221* Pins the data buffer associated with this stream. Returns OK on222* success, NOT_OK on failure, as GetPrimitiveArrayCritical may fail.223*/224static int pinStreamBuffer(JNIEnv *env,225streamBufferPtr sb,226const JOCTET **next_byte) {227if (sb->hstreamBuffer != NULL) {228assert(sb->buf == NULL);229sb->buf =230(JOCTET *)(*env)->GetPrimitiveArrayCritical(env,231sb->hstreamBuffer,232NULL);233if (sb->buf == NULL) {234return NOT_OK;235}236if (sb->bufferOffset != NO_DATA) {237*next_byte = sb->buf + sb->bufferOffset;238}239}240return OK;241}242243/*244* Unpins the data buffer associated with this stream.245*/246static void unpinStreamBuffer(JNIEnv *env,247streamBufferPtr sb,248const JOCTET *next_byte) {249if (sb->buf != NULL) {250assert(sb->hstreamBuffer != NULL);251if (next_byte == NULL) {252sb->bufferOffset = NO_DATA;253} else {254sb->bufferOffset = next_byte - sb->buf;255}256(*env)->ReleasePrimitiveArrayCritical(env,257sb->hstreamBuffer,258sb->buf,2590);260sb->buf = NULL;261}262}263264/*265* Clear out the streamBuffer. This just invalidates the data in the buffer.266*/267static void clearStreamBuffer(streamBufferPtr sb) {268sb->bufferOffset = NO_DATA;269}270271/*************************** end StreamBuffer definition *************/272273/*************************** Pixel Buffer definition ******************/274275typedef struct pixelBufferStruct {276jobject hpixelObject; // Usually a DataBuffer bank as a byte array277unsigned int byteBufferLength;278union pixptr {279INT32 *ip; // Pinned buffer pointer, as 32-bit ints280unsigned char *bp; // Pinned buffer pointer, as bytes281} buf;282} pixelBuffer, *pixelBufferPtr;283284/*285* Initialize a freshly allocated PixelBuffer. All fields are simply286* set to NULL, as we have no idea what size buffer we will need.287*/288static void initPixelBuffer(pixelBufferPtr pb) {289pb->hpixelObject = NULL;290pb->byteBufferLength = 0;291pb->buf.ip = NULL;292}293294/*295* Set the pixelBuffer to use the given buffer, acquiring a new global296* reference for it. Returns OK on success, NOT_OK on failure.297*/298static int setPixelBuffer(JNIEnv *env, pixelBufferPtr pb, jobject obj) {299pb->hpixelObject = (*env)->NewGlobalRef(env, obj);300if (pb->hpixelObject == NULL) {301JNU_ThrowByName( env,302"java/lang/OutOfMemoryError",303"Setting Pixel Buffer");304return NOT_OK;305}306pb->byteBufferLength = (*env)->GetArrayLength(env, pb->hpixelObject);307return OK;308}309310// Forward reference311static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb);312313/*314* Resets a pixel buffer to its initial state. Unpins any pixel buffer,315* releases the global reference, and resets fields to NULL. Use this316* method to dispose the object as well (there is no destroyPixelBuffer).317*/318static void resetPixelBuffer(JNIEnv *env, pixelBufferPtr pb) {319if (pb->hpixelObject != NULL) {320unpinPixelBuffer(env, pb);321(*env)->DeleteGlobalRef(env, pb->hpixelObject);322pb->hpixelObject = NULL;323pb->byteBufferLength = 0;324}325}326327/*328* Pins the data buffer. Returns OK on success, NOT_OK on failure.329*/330static int pinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) {331if (pb->hpixelObject != NULL) {332assert(pb->buf.ip == NULL);333pb->buf.bp = (unsigned char *)(*env)->GetPrimitiveArrayCritical334(env, pb->hpixelObject, NULL);335if (pb->buf.bp == NULL) {336return NOT_OK;337}338}339return OK;340}341342/*343* Unpins the data buffer.344*/345static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) {346347if (pb->buf.ip != NULL) {348assert(pb->hpixelObject != NULL);349(*env)->ReleasePrimitiveArrayCritical(env,350pb->hpixelObject,351pb->buf.ip,3520);353pb->buf.ip = NULL;354}355}356357/********************* end PixelBuffer definition *******************/358359/********************* ImageIOData definition ***********************/360361#define MAX_BANDS 4362#define JPEG_BAND_SIZE 8363#define NUM_BAND_VALUES (1<<JPEG_BAND_SIZE)364#define MAX_JPEG_BAND_VALUE (NUM_BAND_VALUES-1)365#define HALF_MAX_JPEG_BAND_VALUE (MAX_JPEG_BAND_VALUE>>1)366367/* The number of possible incoming values to be scaled. */368#define NUM_INPUT_VALUES (1 << 16)369370/*371* The principal imageioData object, opaque to I/O direction.372* Each JPEGImageReader will have associated with it a373* jpeg_decompress_struct, and similarly each JPEGImageWriter will374* have associated with it a jpeg_compress_struct. In order to375* ensure that these associations persist from one native call to376* the next, and to provide a central locus of imageio-specific377* data, we define an imageioData struct containing references378* to the Java object and the IJG structs. The functions379* that manipulate these objects know whether input or output is being380* performed and therefore know how to manipulate the contents correctly.381* If for some reason they don't, the direction can be determined by382* checking the is_decompressor field of the jpegObj.383* In order for lower level code to determine a384* Java object given an IJG struct, such as for dispatching warnings,385* we use the client_data field of the jpeg object to store a pointer386* to the imageIOData object. Maintenance of this pointer is performed387* exclusively within the following access functions. If you388* change that, you run the risk of dangling pointers.389*/390typedef struct imageIODataStruct {391j_common_ptr jpegObj; // Either struct is fine392jobject imageIOobj; // A JPEGImageReader or a JPEGImageWriter393394streamBuffer streamBuf; // Buffer for the stream395pixelBuffer pixelBuf; // Buffer for pixels396397jboolean abortFlag; // Passed down from Java abort method398} imageIOData, *imageIODataPtr;399400/*401* Allocate and initialize a new imageIOData object to associate the402* jpeg object and the Java object. Returns a pointer to the new object403* on success, NULL on failure.404*/405static imageIODataPtr initImageioData (JNIEnv *env,406j_common_ptr cinfo,407jobject obj) {408409imageIODataPtr data = (imageIODataPtr) malloc (sizeof(imageIOData));410if (data == NULL) {411return NULL;412}413414data->jpegObj = cinfo;415cinfo->client_data = data;416417#ifdef DEBUG_IIO_JPEG418printf("new structures: data is %p, cinfo is %p\n", data, cinfo);419#endif420421data->imageIOobj = (*env)->NewWeakGlobalRef(env, obj);422if (data->imageIOobj == NULL) {423free (data);424return NULL;425}426if (initStreamBuffer(env, &data->streamBuf) == NOT_OK) {427(*env)->DeleteWeakGlobalRef(env, data->imageIOobj);428free (data);429return NULL;430}431initPixelBuffer(&data->pixelBuf);432433data->abortFlag = JNI_FALSE;434435return data;436}437438/*439* Resets the imageIOData object to its initial state, as though440* it had just been allocated and initialized.441*/442static void resetImageIOData(JNIEnv *env, imageIODataPtr data) {443resetStreamBuffer(env, &data->streamBuf);444resetPixelBuffer(env, &data->pixelBuf);445data->abortFlag = JNI_FALSE;446}447448/*449* Releases all resources held by this object and its subobjects,450* frees the object, and returns the jpeg object. This method must451* be called to avoid leaking global references.452* Note that the jpeg object is not freed or destroyed, as that is453* the client's responsibility, although the client_data field is454* cleared.455*/456static j_common_ptr destroyImageioData(JNIEnv *env, imageIODataPtr data) {457j_common_ptr ret = data->jpegObj;458(*env)->DeleteWeakGlobalRef(env, data->imageIOobj);459destroyStreamBuffer(env, &data->streamBuf);460resetPixelBuffer(env, &data->pixelBuf);461ret->client_data = NULL;462free(data);463return ret;464}465466/******************** end ImageIOData definition ***********************/467468/******************** Java array pinning and unpinning *****************/469470/* We use Get/ReleasePrimitiveArrayCritical functions to avoid471* the need to copy array elements for the above two objects.472*473* MAKE SURE TO:474*475* - carefully insert pairs of RELEASE_ARRAYS and GET_ARRAYS around476* callbacks to Java.477* - call RELEASE_ARRAYS before returning to Java.478*479* Otherwise things will go horribly wrong. There may be memory leaks,480* excessive pinning, or even VM crashes!481*482* Note that GetPrimitiveArrayCritical may fail!483*/484485/*486* Release (unpin) all the arrays in use during a read.487*/488static void RELEASE_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET *next_byte)489{490unpinStreamBuffer(env, &data->streamBuf, next_byte);491492unpinPixelBuffer(env, &data->pixelBuf);493494}495496/*497* Get (pin) all the arrays in use during a read.498*/499static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte) {500if (pinStreamBuffer(env, &data->streamBuf, next_byte) == NOT_OK) {501return NOT_OK;502}503504if (pinPixelBuffer(env, &data->pixelBuf) == NOT_OK) {505RELEASE_ARRAYS(env, data, *next_byte);506return NOT_OK;507}508return OK;509}510511/****** end of Java array pinning and unpinning ***********/512513/****** Error Handling *******/514515/*516* Set up error handling to use setjmp/longjmp. This is the third such517* setup, as both the AWT jpeg decoder and the com.sun... JPEG classes518* setup thier own. Ultimately these should be integrated, as they all519* do pretty much the same thing.520*/521522struct sun_jpeg_error_mgr {523struct jpeg_error_mgr pub; /* "public" fields */524525jmp_buf setjmp_buffer; /* for return to caller */526};527528typedef struct sun_jpeg_error_mgr * sun_jpeg_error_ptr;529530/*531* Here's the routine that will replace the standard error_exit method:532*/533534METHODDEF(void)535sun_jpeg_error_exit (j_common_ptr cinfo)536{537/* cinfo->err really points to a sun_jpeg_error_mgr struct */538sun_jpeg_error_ptr myerr = (sun_jpeg_error_ptr) cinfo->err;539540/* For Java, we will format the message and put it in the error we throw. */541542/* Return control to the setjmp point */543longjmp(myerr->setjmp_buffer, 1);544}545546/*547* Error Message handling548*549* This overrides the output_message method to send JPEG messages550*551*/552553METHODDEF(void)554sun_jpeg_output_message (j_common_ptr cinfo)555{556char buffer[JMSG_LENGTH_MAX];557jstring string;558imageIODataPtr data = (imageIODataPtr) cinfo->client_data;559JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);560jobject theObject;561562/* Create the message */563(*cinfo->err->format_message) (cinfo, buffer);564565// Create a new java string from the message566string = (*env)->NewStringUTF(env, buffer);567CHECK_NULL(string);568569theObject = data->imageIOobj;570571if (cinfo->is_decompressor) {572struct jpeg_source_mgr *src = ((j_decompress_ptr)cinfo)->src;573RELEASE_ARRAYS(env, data, src->next_input_byte);574(*env)->CallVoidMethod(env, theObject,575JPEGImageReader_warningWithMessageID,576string);577if ((*env)->ExceptionOccurred(env)578|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {579cinfo->err->error_exit(cinfo);580}581} else {582struct jpeg_destination_mgr *dest = ((j_compress_ptr)cinfo)->dest;583RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));584(*env)->CallVoidMethod(env, theObject,585JPEGImageWriter_warningWithMessageID,586string);587if ((*env)->ExceptionOccurred(env)588|| !GET_ARRAYS(env, data,589(const JOCTET **)(&dest->next_output_byte))) {590cinfo->err->error_exit(cinfo);591}592}593}594595/* End of verbatim copy from jpegdecoder.c */596597/*************** end of error handling *********************/598599/*************** Shared utility code ***********************/600601static void imageio_set_stream(JNIEnv *env,602j_common_ptr cinfo,603imageIODataPtr data,604jobject io){605streamBufferPtr sb;606sun_jpeg_error_ptr jerr;607608sb = &data->streamBuf;609610resetStreamBuffer(env, sb); // Removes any old stream611612/* Now we need a new weak global reference for the I/O provider */613if (io != NULL) { // Fix for 4411955614sb->ioRef = (*env)->NewWeakGlobalRef(env, io);615CHECK_NULL(sb->ioRef);616}617618/* And finally reset state */619data->abortFlag = JNI_FALSE;620621/* Establish the setjmp return context for sun_jpeg_error_exit to use. */622jerr = (sun_jpeg_error_ptr) cinfo->err;623624if (setjmp(jerr->setjmp_buffer)) {625/* If we get here, the JPEG code has signaled an error626while aborting. */627if (!(*env)->ExceptionOccurred(env)) {628char buffer[JMSG_LENGTH_MAX];629(*cinfo->err->format_message) (cinfo,630buffer);631JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);632}633return;634}635636jpeg_abort(cinfo); // Frees any markers, but not tables637638}639640static void imageio_reset(JNIEnv *env,641j_common_ptr cinfo,642imageIODataPtr data) {643sun_jpeg_error_ptr jerr;644645resetImageIOData(env, data); // Mapping to jpeg object is retained.646647/* Establish the setjmp return context for sun_jpeg_error_exit to use. */648jerr = (sun_jpeg_error_ptr) cinfo->err;649650if (setjmp(jerr->setjmp_buffer)) {651/* If we get here, the JPEG code has signaled an error652while aborting. */653if (!(*env)->ExceptionOccurred(env)) {654char buffer[JMSG_LENGTH_MAX];655(*cinfo->err->format_message) (cinfo, buffer);656JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);657}658return;659}660661jpeg_abort(cinfo); // Does not reset tables662663}664665static void imageio_dispose(j_common_ptr info) {666667if (info != NULL) {668free(info->err);669info->err = NULL;670if (info->is_decompressor) {671j_decompress_ptr dinfo = (j_decompress_ptr) info;672free(dinfo->src);673dinfo->src = NULL;674} else {675j_compress_ptr cinfo = (j_compress_ptr) info;676free(cinfo->dest);677cinfo->dest = NULL;678}679jpeg_destroy(info);680free(info);681}682}683684static void imageio_abort(JNIEnv *env, jobject this,685imageIODataPtr data) {686data->abortFlag = JNI_TRUE;687}688689static int setQTables(JNIEnv *env,690j_common_ptr cinfo,691jobjectArray qtables,692boolean write) {693jsize qlen;694jobject table;695jintArray qdata;696jint *qdataBody;697JQUANT_TBL *quant_ptr;698int i, j;699j_compress_ptr comp;700j_decompress_ptr decomp;701702qlen = (*env)->GetArrayLength(env, qtables);703#ifdef DEBUG_IIO_JPEG704printf("in setQTables, qlen = %d, write is %d\n", qlen, write);705#endif706if (qlen > NUM_QUANT_TBLS) {707/* Ignore extra qunterization tables. */708qlen = NUM_QUANT_TBLS;709}710for (i = 0; i < qlen; i++) {711table = (*env)->GetObjectArrayElement(env, qtables, i);712CHECK_NULL_RETURN(table, 0);713qdata = (*env)->GetObjectField(env, table, JPEGQTable_tableID);714qdataBody = (*env)->GetPrimitiveArrayCritical(env, qdata, NULL);715716if (cinfo->is_decompressor) {717decomp = (j_decompress_ptr) cinfo;718if (decomp->quant_tbl_ptrs[i] == NULL) {719decomp->quant_tbl_ptrs[i] =720jpeg_alloc_quant_table(cinfo);721}722quant_ptr = decomp->quant_tbl_ptrs[i];723} else {724comp = (j_compress_ptr) cinfo;725if (comp->quant_tbl_ptrs[i] == NULL) {726comp->quant_tbl_ptrs[i] =727jpeg_alloc_quant_table(cinfo);728}729quant_ptr = comp->quant_tbl_ptrs[i];730}731732for (j = 0; j < 64; j++) {733quant_ptr->quantval[j] = (UINT16)qdataBody[j];734}735quant_ptr->sent_table = !write;736(*env)->ReleasePrimitiveArrayCritical(env,737qdata,738qdataBody,7390);740}741return qlen;742}743744static boolean setHuffTable(JNIEnv *env,745JHUFF_TBL *huff_ptr,746jobject table) {747748jshortArray huffLens;749jshortArray huffValues;750jshort *hlensBody, *hvalsBody;751jsize hlensLen, hvalsLen;752int i;753754// lengths755huffLens = (*env)->GetObjectField(env,756table,757JPEGHuffmanTable_lengthsID);758hlensLen = (*env)->GetArrayLength(env, huffLens);759hlensBody = (*env)->GetShortArrayElements(env,760huffLens,761NULL);762CHECK_NULL_RETURN(hlensBody, FALSE);763764if (hlensLen > 16) {765/* Ignore extra elements of bits array. Only 16 elements can be766stored. 0-th element is not used. (see jpeglib.h, line 107) */767hlensLen = 16;768}769for (i = 1; i <= hlensLen; i++) {770huff_ptr->bits[i] = (UINT8)hlensBody[i-1];771}772(*env)->ReleaseShortArrayElements(env,773huffLens,774hlensBody,775JNI_ABORT);776// values777huffValues = (*env)->GetObjectField(env,778table,779JPEGHuffmanTable_valuesID);780hvalsLen = (*env)->GetArrayLength(env, huffValues);781hvalsBody = (*env)->GetShortArrayElements(env,782huffValues,783NULL);784CHECK_NULL_RETURN(hvalsBody, FALSE);785786if (hvalsLen > 256) {787/* Ignore extra elements of hufval array. Only 256 elements788can be stored. (see jpeglib.h, line 109) */789hlensLen = 256;790}791for (i = 0; i < hvalsLen; i++) {792huff_ptr->huffval[i] = (UINT8)hvalsBody[i];793}794(*env)->ReleaseShortArrayElements(env,795huffValues,796hvalsBody,797JNI_ABORT);798return TRUE;799}800801static int setHTables(JNIEnv *env,802j_common_ptr cinfo,803jobjectArray DCHuffmanTables,804jobjectArray ACHuffmanTables,805boolean write) {806int i;807jobject table;808JHUFF_TBL *huff_ptr;809j_compress_ptr comp;810j_decompress_ptr decomp;811jsize hlen = (*env)->GetArrayLength(env, DCHuffmanTables);812813if (hlen > NUM_HUFF_TBLS) {814/* Ignore extra DC huffman tables. */815hlen = NUM_HUFF_TBLS;816}817for (i = 0; i < hlen; i++) {818if (cinfo->is_decompressor) {819decomp = (j_decompress_ptr) cinfo;820if (decomp->dc_huff_tbl_ptrs[i] == NULL) {821decomp->dc_huff_tbl_ptrs[i] =822jpeg_alloc_huff_table(cinfo);823}824huff_ptr = decomp->dc_huff_tbl_ptrs[i];825} else {826comp = (j_compress_ptr) cinfo;827if (comp->dc_huff_tbl_ptrs[i] == NULL) {828comp->dc_huff_tbl_ptrs[i] =829jpeg_alloc_huff_table(cinfo);830}831huff_ptr = comp->dc_huff_tbl_ptrs[i];832}833table = (*env)->GetObjectArrayElement(env, DCHuffmanTables, i);834if (table == NULL || !setHuffTable(env, huff_ptr, table)) {835return 0;836}837huff_ptr->sent_table = !write;838}839hlen = (*env)->GetArrayLength(env, ACHuffmanTables);840if (hlen > NUM_HUFF_TBLS) {841/* Ignore extra AC huffman tables. */842hlen = NUM_HUFF_TBLS;843}844for (i = 0; i < hlen; i++) {845if (cinfo->is_decompressor) {846decomp = (j_decompress_ptr) cinfo;847if (decomp->ac_huff_tbl_ptrs[i] == NULL) {848decomp->ac_huff_tbl_ptrs[i] =849jpeg_alloc_huff_table(cinfo);850}851huff_ptr = decomp->ac_huff_tbl_ptrs[i];852} else {853comp = (j_compress_ptr) cinfo;854if (comp->ac_huff_tbl_ptrs[i] == NULL) {855comp->ac_huff_tbl_ptrs[i] =856jpeg_alloc_huff_table(cinfo);857}858huff_ptr = comp->ac_huff_tbl_ptrs[i];859}860table = (*env)->GetObjectArrayElement(env, ACHuffmanTables, i);861if(table == NULL || !setHuffTable(env, huff_ptr, table)) {862return 0;863}864huff_ptr->sent_table = !write;865}866return hlen;867}868869870/*************** end of shared utility code ****************/871872/********************** Reader Support **************************/873874/********************** Source Management ***********************/875876/*877* INPUT HANDLING:878*879* The JPEG library's input management is defined by the jpeg_source_mgr880* structure which contains two fields to convey the information in the881* buffer and 5 methods which perform all buffer management. The library882* defines a standard input manager that uses stdio for obtaining compressed883* jpeg data, but here we need to use Java to get our data.884*885* We use the library jpeg_source_mgr but our own routines that access886* imageio-specific information in the imageIOData structure.887*/888889/*890* Initialize source. This is called by jpeg_read_header() before any891* data is actually read. Unlike init_destination(), it may leave892* bytes_in_buffer set to 0 (in which case a fill_input_buffer() call893* will occur immediately).894*/895896GLOBAL(void)897imageio_init_source(j_decompress_ptr cinfo)898{899struct jpeg_source_mgr *src = cinfo->src;900src->next_input_byte = NULL;901src->bytes_in_buffer = 0;902}903904/*905* This is called whenever bytes_in_buffer has reached zero and more906* data is wanted. In typical applications, it should read fresh data907* into the buffer (ignoring the current state of next_input_byte and908* bytes_in_buffer), reset the pointer & count to the start of the909* buffer, and return TRUE indicating that the buffer has been reloaded.910* It is not necessary to fill the buffer entirely, only to obtain at911* least one more byte. bytes_in_buffer MUST be set to a positive value912* if TRUE is returned. A FALSE return should only be used when I/O913* suspension is desired (this mode is discussed in the next section).914*/915/*916* Note that with I/O suspension turned on, this procedure should not917* do any work since the JPEG library has a very simple backtracking918* mechanism which relies on the fact that the buffer will be filled919* only when it has backed out to the top application level. When920* suspendable is turned on, imageio_fill_suspended_buffer will921* do the actual work of filling the buffer.922*/923924GLOBAL(boolean)925imageio_fill_input_buffer(j_decompress_ptr cinfo)926{927struct jpeg_source_mgr *src = cinfo->src;928imageIODataPtr data = (imageIODataPtr) cinfo->client_data;929streamBufferPtr sb = &data->streamBuf;930JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);931int ret;932jobject input = NULL;933934/* This is where input suspends */935if (sb->suspendable) {936return FALSE;937}938939#ifdef DEBUG_IIO_JPEG940printf("Filling input buffer, remaining skip is %ld, ",941sb->remaining_skip);942printf("Buffer length is %d\n", sb->bufferLength);943#endif944945/*946* Definitively skips. Could be left over if we tried to skip947* more than a buffer's worth but suspended when getting the next948* buffer. Now we aren't suspended, so we can catch up.949*/950if (sb->remaining_skip) {951src->skip_input_data(cinfo, 0);952}953954/*955* Now fill a complete buffer, or as much of one as the stream956* will give us if we are near the end.957*/958RELEASE_ARRAYS(env, data, src->next_input_byte);959960GET_IO_REF(input);961962ret = (*env)->CallIntMethod(env,963input,964JPEGImageReader_readInputDataID,965sb->hstreamBuffer, 0,966sb->bufferLength);967if ((ret > 0) && ((unsigned int)ret > sb->bufferLength)) {968ret = (int)sb->bufferLength;969}970if ((*env)->ExceptionOccurred(env)971|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {972cinfo->err->error_exit((j_common_ptr) cinfo);973}974975#ifdef DEBUG_IIO_JPEG976printf("Buffer filled. ret = %d\n", ret);977#endif978/*979* If we have reached the end of the stream, then the EOI marker980* is missing. We accept such streams but generate a warning.981* The image is likely to be corrupted, though everything through982* the end of the last complete MCU should be usable.983*/984if (ret <= 0) {985jobject reader = data->imageIOobj;986#ifdef DEBUG_IIO_JPEG987printf("YO! Early EOI! ret = %d\n", ret);988#endif989RELEASE_ARRAYS(env, data, src->next_input_byte);990(*env)->CallVoidMethod(env, reader,991JPEGImageReader_warningOccurredID,992READ_NO_EOI);993if ((*env)->ExceptionOccurred(env)994|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {995cinfo->err->error_exit((j_common_ptr) cinfo);996}997998sb->buf[0] = (JOCTET) 0xFF;999sb->buf[1] = (JOCTET) JPEG_EOI;1000ret = 2;1001}10021003src->next_input_byte = sb->buf;1004src->bytes_in_buffer = ret;10051006return TRUE;1007}10081009/*1010* With I/O suspension turned on, the JPEG library requires that all1011* buffer filling be done at the top application level, using this1012* function. Due to the way that backtracking works, this procedure1013* saves all of the data that was left in the buffer when suspension1014* occurred and read new data only at the end.1015*/10161017GLOBAL(void)1018imageio_fill_suspended_buffer(j_decompress_ptr cinfo)1019{1020struct jpeg_source_mgr *src = cinfo->src;1021imageIODataPtr data = (imageIODataPtr) cinfo->client_data;1022streamBufferPtr sb = &data->streamBuf;1023JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);1024jint ret;1025size_t offset, buflen;1026jobject input = NULL;10271028/*1029* The original (jpegdecoder.c) had code here that called1030* InputStream.available and just returned if the number of bytes1031* available was less than any remaining skip. Presumably this was1032* to avoid blocking, although the benefit was unclear, as no more1033* decompression can take place until more data is available, so1034* the code would block on input a little further along anyway.1035* ImageInputStreams don't have an available method, so we'll just1036* block in the skip if we have to.1037*/10381039if (sb->remaining_skip) {1040src->skip_input_data(cinfo, 0);1041}10421043/* Save the data currently in the buffer */1044offset = src->bytes_in_buffer;1045if (src->next_input_byte > sb->buf) {1046memcpy(sb->buf, src->next_input_byte, offset);1047}104810491050RELEASE_ARRAYS(env, data, src->next_input_byte);10511052GET_IO_REF(input);10531054buflen = sb->bufferLength - offset;1055if (buflen <= 0) {1056if (!GET_ARRAYS(env, data, &(src->next_input_byte))) {1057cinfo->err->error_exit((j_common_ptr) cinfo);1058}1059RELEASE_ARRAYS(env, data, src->next_input_byte);1060return;1061}10621063ret = (*env)->CallIntMethod(env, input,1064JPEGImageReader_readInputDataID,1065sb->hstreamBuffer,1066offset, buflen);1067if ((ret > 0) && ((unsigned int)ret > buflen)) ret = (int)buflen;1068if ((*env)->ExceptionOccurred(env)1069|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1070cinfo->err->error_exit((j_common_ptr) cinfo);1071}1072/*1073* If we have reached the end of the stream, then the EOI marker1074* is missing. We accept such streams but generate a warning.1075* The image is likely to be corrupted, though everything through1076* the end of the last complete MCU should be usable.1077*/1078if (ret <= 0) {1079jobject reader = data->imageIOobj;1080RELEASE_ARRAYS(env, data, src->next_input_byte);1081(*env)->CallVoidMethod(env, reader,1082JPEGImageReader_warningOccurredID,1083READ_NO_EOI);1084if ((*env)->ExceptionOccurred(env)1085|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1086cinfo->err->error_exit((j_common_ptr) cinfo);1087}10881089sb->buf[offset] = (JOCTET) 0xFF;1090sb->buf[offset + 1] = (JOCTET) JPEG_EOI;1091ret = 2;1092}10931094src->next_input_byte = sb->buf;1095src->bytes_in_buffer = ret + offset;10961097return;1098}10991100/*1101* Skip num_bytes worth of data. The buffer pointer and count are1102* advanced over num_bytes input bytes, using the input stream1103* skipBytes method if the skip is greater than the number of bytes1104* in the buffer. This is used to skip over a potentially large amount of1105* uninteresting data (such as an APPn marker). bytes_in_buffer will be1106* zero on return if the skip is larger than the current contents of the1107* buffer.1108*1109* A negative skip count is treated as a no-op. A zero skip count1110* skips any remaining skip from a previous skip while suspended.1111*1112* Note that with I/O suspension turned on, this procedure does not1113* call skipBytes since the JPEG library has a very simple backtracking1114* mechanism which relies on the fact that the application level has1115* exclusive control over actual I/O.1116*/11171118GLOBAL(void)1119imageio_skip_input_data(j_decompress_ptr cinfo, long num_bytes)1120{1121struct jpeg_source_mgr *src = cinfo->src;1122imageIODataPtr data = (imageIODataPtr) cinfo->client_data;1123streamBufferPtr sb = &data->streamBuf;1124JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);1125jlong ret;1126jobject reader;1127jobject input = NULL;11281129if (num_bytes < 0) {1130return;1131}1132num_bytes += sb->remaining_skip;1133sb->remaining_skip = 0;11341135/* First the easy case where we are skipping <= the current contents. */1136ret = src->bytes_in_buffer;1137if (ret >= num_bytes) {1138src->next_input_byte += num_bytes;1139src->bytes_in_buffer -= num_bytes;1140return;1141}11421143/*1144* We are skipping more than is in the buffer. We empty the buffer and,1145* if we aren't suspended, call the Java skipBytes method. We always1146* leave the buffer empty, to be filled by either fill method above.1147*/1148src->bytes_in_buffer = 0;1149src->next_input_byte = sb->buf;11501151num_bytes -= (long)ret;1152if (sb->suspendable) {1153sb->remaining_skip = num_bytes;1154return;1155}11561157RELEASE_ARRAYS(env, data, src->next_input_byte);11581159GET_IO_REF(input);11601161ret = (*env)->CallLongMethod(env,1162input,1163JPEGImageReader_skipInputBytesID,1164(jlong) num_bytes);1165if ((*env)->ExceptionOccurred(env)1166|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1167cinfo->err->error_exit((j_common_ptr) cinfo);1168}11691170/*1171* If we have reached the end of the stream, then the EOI marker1172* is missing. We accept such streams but generate a warning.1173* The image is likely to be corrupted, though everything through1174* the end of the last complete MCU should be usable.1175*/1176if (ret <= 0) {1177reader = data->imageIOobj;1178RELEASE_ARRAYS(env, data, src->next_input_byte);1179(*env)->CallVoidMethod(env,1180reader,1181JPEGImageReader_warningOccurredID,1182READ_NO_EOI);11831184if ((*env)->ExceptionOccurred(env)1185|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1186cinfo->err->error_exit((j_common_ptr) cinfo);1187}1188sb->buf[0] = (JOCTET) 0xFF;1189sb->buf[1] = (JOCTET) JPEG_EOI;1190src->bytes_in_buffer = 2;1191src->next_input_byte = sb->buf;1192}1193}11941195/*1196* Terminate source --- called by jpeg_finish_decompress() after all1197* data for an image has been read. In our case pushes back any1198* remaining data, as it will be for another image and must be available1199* for java to find out that there is another image. Also called if1200* reseting state after reading a tables-only image.1201*/12021203GLOBAL(void)1204imageio_term_source(j_decompress_ptr cinfo)1205{1206// To pushback, just seek back by src->bytes_in_buffer1207struct jpeg_source_mgr *src = cinfo->src;1208imageIODataPtr data = (imageIODataPtr) cinfo->client_data;1209JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);1210jobject reader = data->imageIOobj;1211if (src->bytes_in_buffer > 0) {1212RELEASE_ARRAYS(env, data, src->next_input_byte);1213(*env)->CallVoidMethod(env,1214reader,1215JPEGImageReader_pushBackID,1216src->bytes_in_buffer);12171218if ((*env)->ExceptionOccurred(env)1219|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1220cinfo->err->error_exit((j_common_ptr) cinfo);1221}1222src->bytes_in_buffer = 0;1223//src->next_input_byte = sb->buf;1224}1225}12261227/********************* end of source manager ******************/12281229/********************* ICC profile support ********************/1230/*1231* The following routines are modified versions of the ICC1232* profile support routines available from the IJG website.1233* The originals were written by Todd Newman1234* <[email protected]> and modified by Tom Lane for1235* the IJG. They are further modified to fit in the context1236* of the imageio JPEG plug-in.1237*/12381239/*1240* Since an ICC profile can be larger than the maximum size of a JPEG marker1241* (64K), we need provisions to split it into multiple markers. The format1242* defined by the ICC specifies one or more APP2 markers containing the1243* following data:1244* Identifying string ASCII "ICC_PROFILE\0" (12 bytes)1245* Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)1246* Number of markers Total number of APP2's used (1 byte)1247* Profile data (remainder of APP2 data)1248* Decoders should use the marker sequence numbers to reassemble the profile,1249* rather than assuming that the APP2 markers appear in the correct sequence.1250*/12511252#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */1253#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */1254#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */1255#define MAX_DATA_BYTES_IN_ICC_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)125612571258/*1259* Handy subroutine to test whether a saved marker is an ICC profile marker.1260*/12611262static boolean1263marker_is_icc (jpeg_saved_marker_ptr marker)1264{1265return1266marker->marker == ICC_MARKER &&1267marker->data_length >= ICC_OVERHEAD_LEN &&1268/* verify the identifying string */1269GETJOCTET(marker->data[0]) == 0x49 &&1270GETJOCTET(marker->data[1]) == 0x43 &&1271GETJOCTET(marker->data[2]) == 0x43 &&1272GETJOCTET(marker->data[3]) == 0x5F &&1273GETJOCTET(marker->data[4]) == 0x50 &&1274GETJOCTET(marker->data[5]) == 0x52 &&1275GETJOCTET(marker->data[6]) == 0x4F &&1276GETJOCTET(marker->data[7]) == 0x46 &&1277GETJOCTET(marker->data[8]) == 0x49 &&1278GETJOCTET(marker->data[9]) == 0x4C &&1279GETJOCTET(marker->data[10]) == 0x45 &&1280GETJOCTET(marker->data[11]) == 0x0;1281}12821283/*1284* See if there was an ICC profile in the JPEG file being read;1285* if so, reassemble and return the profile data as a new Java byte array.1286* If there was no ICC profile, return NULL.1287*1288* If the file contains invalid ICC APP2 markers, we throw an IIOException1289* with an appropriate message.1290*/12911292jbyteArray1293read_icc_profile (JNIEnv *env, j_decompress_ptr cinfo)1294{1295jpeg_saved_marker_ptr marker;1296int num_markers = 0;1297int num_found_markers = 0;1298int seq_no;1299JOCTET *icc_data;1300JOCTET *dst_ptr;1301unsigned int total_length;1302#define MAX_SEQ_NO 255 // sufficient since marker numbers are bytes1303jpeg_saved_marker_ptr icc_markers[MAX_SEQ_NO + 1];1304int first; // index of the first marker in the icc_markers array1305int last; // index of the last marker in the icc_markers array1306jbyteArray data = NULL;13071308/* This first pass over the saved markers discovers whether there are1309* any ICC markers and verifies the consistency of the marker numbering.1310*/13111312for (seq_no = 0; seq_no <= MAX_SEQ_NO; seq_no++)1313icc_markers[seq_no] = NULL;131413151316for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {1317if (marker_is_icc(marker)) {1318if (num_markers == 0)1319num_markers = GETJOCTET(marker->data[13]);1320else if (num_markers != GETJOCTET(marker->data[13])) {1321JNU_ThrowByName(env, "javax/imageio/IIOException",1322"Invalid icc profile: inconsistent num_markers fields");1323return NULL;1324}1325seq_no = GETJOCTET(marker->data[12]);13261327/* Some third-party tools produce images with profile chunk1328* numeration started from zero. It is inconsistent with ICC1329* spec, but seems to be recognized by majority of image1330* processing tools, so we should be more tolerant to this1331* departure from the spec.1332*/1333if (seq_no < 0 || seq_no > num_markers) {1334JNU_ThrowByName(env, "javax/imageio/IIOException",1335"Invalid icc profile: bad sequence number");1336return NULL;1337}1338if (icc_markers[seq_no] != NULL) {1339JNU_ThrowByName(env, "javax/imageio/IIOException",1340"Invalid icc profile: duplicate sequence numbers");1341return NULL;1342}1343icc_markers[seq_no] = marker;1344num_found_markers ++;1345}1346}13471348if (num_markers == 0)1349return NULL; // There is no profile13501351if (num_markers != num_found_markers) {1352JNU_ThrowByName(env, "javax/imageio/IIOException",1353"Invalid icc profile: invalid number of icc markers");1354return NULL;1355}13561357first = icc_markers[0] ? 0 : 1;1358last = num_found_markers + first;13591360/* Check for missing markers, count total space needed.1361*/1362total_length = 0;1363for (seq_no = first; seq_no < last; seq_no++) {1364unsigned int length;1365if (icc_markers[seq_no] == NULL) {1366JNU_ThrowByName(env, "javax/imageio/IIOException",1367"Invalid icc profile: missing sequence number");1368return NULL;1369}1370/* check the data length correctness */1371length = icc_markers[seq_no]->data_length;1372if (ICC_OVERHEAD_LEN > length || length > MAX_BYTES_IN_MARKER) {1373JNU_ThrowByName(env, "javax/imageio/IIOException",1374"Invalid icc profile: invalid data length");1375return NULL;1376}1377total_length += (length - ICC_OVERHEAD_LEN);1378}13791380if (total_length <= 0) {1381JNU_ThrowByName(env, "javax/imageio/IIOException",1382"Invalid icc profile: found only empty markers");1383return NULL;1384}13851386/* Allocate a Java byte array for assembled data */13871388data = (*env)->NewByteArray(env, total_length);1389if (data == NULL) {1390JNU_ThrowByName(env,1391"java/lang/OutOfMemoryError",1392"Reading ICC profile");1393return NULL;1394}13951396icc_data = (JOCTET *)(*env)->GetPrimitiveArrayCritical(env,1397data,1398NULL);1399if (icc_data == NULL) {1400JNU_ThrowByName(env, "javax/imageio/IIOException",1401"Unable to pin icc profile data array");1402return NULL;1403}14041405/* and fill it in */1406dst_ptr = icc_data;1407for (seq_no = first; seq_no < last; seq_no++) {1408JOCTET FAR *src_ptr = icc_markers[seq_no]->data + ICC_OVERHEAD_LEN;1409unsigned int length =1410icc_markers[seq_no]->data_length - ICC_OVERHEAD_LEN;14111412memcpy(dst_ptr, src_ptr, length);1413dst_ptr += length;1414}14151416/* finally, unpin the array */1417(*env)->ReleasePrimitiveArrayCritical(env,1418data,1419icc_data,14200);142114221423return data;1424}14251426/********************* end of ICC profile support *************/14271428/********************* Reader JNI calls ***********************/14291430JNIEXPORT void JNICALL1431Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_initReaderIDs1432(JNIEnv *env,1433jclass cls,1434jclass ImageInputStreamClass,1435jclass qTableClass,1436jclass huffClass) {14371438CHECK_NULL(JPEGImageReader_readInputDataID = (*env)->GetMethodID(env,1439cls,1440"readInputData",1441"([BII)I"));1442CHECK_NULL(JPEGImageReader_skipInputBytesID = (*env)->GetMethodID(env,1443cls,1444"skipInputBytes",1445"(J)J"));1446CHECK_NULL(JPEGImageReader_warningOccurredID = (*env)->GetMethodID(env,1447cls,1448"warningOccurred",1449"(I)V"));1450CHECK_NULL(JPEGImageReader_warningWithMessageID =1451(*env)->GetMethodID(env,1452cls,1453"warningWithMessage",1454"(Ljava/lang/String;)V"));1455CHECK_NULL(JPEGImageReader_setImageDataID = (*env)->GetMethodID(env,1456cls,1457"setImageData",1458"(IIIII[B)V"));1459CHECK_NULL(JPEGImageReader_acceptPixelsID = (*env)->GetMethodID(env,1460cls,1461"acceptPixels",1462"(IZ)V"));1463CHECK_NULL(JPEGImageReader_passStartedID = (*env)->GetMethodID(env,1464cls,1465"passStarted",1466"(I)V"));1467CHECK_NULL(JPEGImageReader_passCompleteID = (*env)->GetMethodID(env,1468cls,1469"passComplete",1470"()V"));1471CHECK_NULL(JPEGImageReader_pushBackID = (*env)->GetMethodID(env,1472cls,1473"pushBack",1474"(I)V"));1475CHECK_NULL(JPEGImageReader_skipPastImageID = (*env)->GetMethodID(env,1476cls,1477"skipPastImage",1478"(I)V"));1479CHECK_NULL(JPEGQTable_tableID = (*env)->GetFieldID(env,1480qTableClass,1481"qTable",1482"[I"));14831484CHECK_NULL(JPEGHuffmanTable_lengthsID = (*env)->GetFieldID(env,1485huffClass,1486"lengths",1487"[S"));14881489CHECK_NULL(JPEGHuffmanTable_valuesID = (*env)->GetFieldID(env,1490huffClass,1491"values",1492"[S"));1493}14941495JNIEXPORT jlong JNICALL1496Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_initJPEGImageReader1497(JNIEnv *env,1498jobject this) {14991500imageIODataPtr ret;1501struct sun_jpeg_error_mgr *jerr;15021503/* This struct contains the JPEG decompression parameters and pointers to1504* working space (which is allocated as needed by the JPEG library).1505*/1506struct jpeg_decompress_struct *cinfo =1507malloc(sizeof(struct jpeg_decompress_struct));1508if (cinfo == NULL) {1509JNU_ThrowByName( env,1510"java/lang/OutOfMemoryError",1511"Initializing Reader");1512return 0;1513}15141515/* We use our private extension JPEG error handler.1516*/1517jerr = malloc (sizeof(struct sun_jpeg_error_mgr));1518if (jerr == NULL) {1519JNU_ThrowByName( env,1520"java/lang/OutOfMemoryError",1521"Initializing Reader");1522free(cinfo);1523return 0;1524}15251526/* We set up the normal JPEG error routines, then override error_exit. */1527cinfo->err = jpeg_std_error(&(jerr->pub));1528jerr->pub.error_exit = sun_jpeg_error_exit;1529/* We need to setup our own print routines */1530jerr->pub.output_message = sun_jpeg_output_message;1531/* Now we can setjmp before every call to the library */15321533/* Establish the setjmp return context for sun_jpeg_error_exit to use. */1534if (setjmp(jerr->setjmp_buffer)) {1535/* If we get here, the JPEG code has signaled an error. */1536char buffer[JMSG_LENGTH_MAX];1537(*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,1538buffer);1539JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);1540return 0;1541}15421543/* Perform library initialization */1544jpeg_create_decompress(cinfo);15451546// Set up to keep any APP2 markers, as these might contain ICC profile1547// data1548jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF);15491550/*1551* Now set up our source.1552*/1553cinfo->src =1554(struct jpeg_source_mgr *) malloc (sizeof(struct jpeg_source_mgr));1555if (cinfo->src == NULL) {1556JNU_ThrowByName(env,1557"java/lang/OutOfMemoryError",1558"Initializing Reader");1559imageio_dispose((j_common_ptr)cinfo);1560return 0;1561}1562cinfo->src->bytes_in_buffer = 0;1563cinfo->src->next_input_byte = NULL;1564cinfo->src->init_source = imageio_init_source;1565cinfo->src->fill_input_buffer = imageio_fill_input_buffer;1566cinfo->src->skip_input_data = imageio_skip_input_data;1567cinfo->src->resync_to_restart = jpeg_resync_to_restart; // use default1568cinfo->src->term_source = imageio_term_source;15691570/* set up the association to persist for future calls */1571ret = initImageioData(env, (j_common_ptr) cinfo, this);1572if (ret == NULL) {1573(*env)->ExceptionClear(env);1574JNU_ThrowByName(env, "java/lang/OutOfMemoryError",1575"Initializing Reader");1576imageio_dispose((j_common_ptr)cinfo);1577return 0;1578}1579return ptr_to_jlong(ret);1580}15811582/*1583* When we set a source from Java, we set up the stream in the streamBuf1584* object. If there was an old one, it is released first.1585*/15861587JNIEXPORT void JNICALL1588Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setSource1589(JNIEnv *env,1590jobject this,1591jlong ptr) {15921593imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);1594j_common_ptr cinfo;15951596if (data == NULL) {1597JNU_ThrowByName(env,1598"java/lang/IllegalStateException",1599"Attempting to use reader after dispose()");1600return;1601}16021603cinfo = data->jpegObj;16041605imageio_set_stream(env, cinfo, data, this);16061607imageio_init_source((j_decompress_ptr) cinfo);1608}16091610#define JPEG_APP1 (JPEG_APP0 + 1) /* EXIF APP1 marker code */16111612/*1613* For EXIF images, the APP1 will appear immediately after the SOI,1614* so it's safe to only look at the first marker in the list.1615* (see http://www.exif.org/Exif2-2.PDF, section 4.7, page 58)1616*/1617#define IS_EXIF(c) \1618(((c)->marker_list != NULL) && ((c)->marker_list->marker == JPEG_APP1))16191620JNIEXPORT jboolean JNICALL1621Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader1622(JNIEnv *env,1623jobject this,1624jlong ptr,1625jboolean clearFirst,1626jboolean reset) {16271628int ret;1629int h_samp0, h_samp1, h_samp2;1630int v_samp0, v_samp1, v_samp2;1631int cid0, cid1, cid2;1632jboolean retval = JNI_FALSE;1633imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);1634j_decompress_ptr cinfo;1635struct jpeg_source_mgr *src;1636sun_jpeg_error_ptr jerr;1637jbyteArray profileData = NULL;16381639if (data == NULL) {1640JNU_ThrowByName(env,1641"java/lang/IllegalStateException",1642"Attempting to use reader after dispose()");1643return JNI_FALSE;1644}16451646cinfo = (j_decompress_ptr) data->jpegObj;1647src = cinfo->src;16481649/* Establish the setjmp return context for sun_jpeg_error_exit to use. */1650jerr = (sun_jpeg_error_ptr) cinfo->err;16511652if (setjmp(jerr->setjmp_buffer)) {1653/* If we get here, the JPEG code has signaled an error1654while reading the header. */1655RELEASE_ARRAYS(env, data, src->next_input_byte);1656if (!(*env)->ExceptionOccurred(env)) {1657char buffer[JMSG_LENGTH_MAX];1658(*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,1659buffer);1660JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);1661}1662return retval;1663}16641665#ifdef DEBUG_IIO_JPEG1666printf("In readImageHeader, data is %p cinfo is %p\n", data, cinfo);1667printf("clearFirst is %d\n", clearFirst);1668#endif16691670if (GET_ARRAYS(env, data, &src->next_input_byte) == NOT_OK) {1671(*env)->ExceptionClear(env);1672JNU_ThrowByName(env,1673"javax/imageio/IIOException",1674"Array pin failed");1675return retval;1676}16771678/*1679* Now clear the input buffer if the Java code has done a seek1680* on the stream since the last call, invalidating any buffer contents.1681*/1682if (clearFirst) {1683clearStreamBuffer(&data->streamBuf);1684src->next_input_byte = NULL;1685src->bytes_in_buffer = 0;1686}16871688ret = jpeg_read_header(cinfo, FALSE);16891690if (ret == JPEG_HEADER_TABLES_ONLY) {1691retval = JNI_TRUE;1692imageio_term_source(cinfo); // Pushback remaining buffer contents1693#ifdef DEBUG_IIO_JPEG1694printf("just read tables-only image; q table 0 at %p\n",1695cinfo->quant_tbl_ptrs[0]);1696#endif1697RELEASE_ARRAYS(env, data, src->next_input_byte);1698} else {1699/*1700* Now adjust the jpeg_color_space variable, which was set in1701* default_decompress_parms, to reflect our differences from IJG1702*/17031704switch (cinfo->jpeg_color_space) {1705default :1706break;1707case JCS_YCbCr:17081709/*1710* There are several possibilities:1711* - we got image with embeded colorspace1712* Use it. User knows what he is doing.1713* - we got JFIF image1714* Must be YCbCr (see http://www.w3.org/Graphics/JPEG/jfif3.pdf, page 2)1715* - we got EXIF image1716* Must be YCbCr (see http://www.exif.org/Exif2-2.PDF, section 4.7, page 63)1717* - something else1718* Apply heuristical rules to identify actual colorspace.1719*/17201721if (cinfo->saw_Adobe_marker) {1722if (cinfo->Adobe_transform != 1) {1723/*1724* IJG guesses this is YCbCr and emits a warning1725* We would rather not guess. Then the user knows1726* To read this as a Raster if at all1727*/1728cinfo->jpeg_color_space = JCS_UNKNOWN;1729cinfo->out_color_space = JCS_UNKNOWN;1730}1731} else if (!cinfo->saw_JFIF_marker && !IS_EXIF(cinfo)) {1732/*1733* In the absence of certain markers, IJG has interpreted1734* component id's of [1,2,3] as meaning YCbCr.We follow that1735* interpretation, which is additionally described in the Image1736* I/O JPEG metadata spec.If that condition is not met here the1737* next step will be to examine the subsampling factors, if1738* there is any difference in subsampling factors we also assume1739* YCbCr, only if both horizontal and vertical subsampling1740* is same we assume JPEG color space as RGB.1741* This is also described in the Image I/O JPEG metadata spec.1742*/1743h_samp0 = cinfo->comp_info[0].h_samp_factor;1744h_samp1 = cinfo->comp_info[1].h_samp_factor;1745h_samp2 = cinfo->comp_info[2].h_samp_factor;17461747v_samp0 = cinfo->comp_info[0].v_samp_factor;1748v_samp1 = cinfo->comp_info[1].v_samp_factor;1749v_samp2 = cinfo->comp_info[2].v_samp_factor;17501751cid0 = cinfo->comp_info[0].component_id;1752cid1 = cinfo->comp_info[1].component_id;1753cid2 = cinfo->comp_info[2].component_id;17541755if ((!(cid0 == 1 && cid1 == 2 && cid2 == 3)) &&1756((h_samp1 == h_samp0) && (h_samp2 == h_samp0) &&1757(v_samp1 == v_samp0) && (v_samp2 == v_samp0)))1758{1759cinfo->jpeg_color_space = JCS_RGB;1760/* output is already RGB, so it stays the same */1761}1762}1763break;1764case JCS_YCCK:1765if ((cinfo->saw_Adobe_marker) && (cinfo->Adobe_transform != 2)) {1766/*1767* IJG guesses this is YCCK and emits a warning1768* We would rather not guess. Then the user knows1769* To read this as a Raster if at all1770*/1771cinfo->jpeg_color_space = JCS_UNKNOWN;1772cinfo->out_color_space = JCS_UNKNOWN;1773}1774break;1775case JCS_CMYK:1776/*1777* IJG assumes all unidentified 4-channels are CMYK.1778* We assume that only if the second two channels are1779* not subsampled (either horizontally or vertically).1780* If they are, we assume YCCK.1781*/1782h_samp0 = cinfo->comp_info[0].h_samp_factor;1783h_samp1 = cinfo->comp_info[1].h_samp_factor;1784h_samp2 = cinfo->comp_info[2].h_samp_factor;17851786v_samp0 = cinfo->comp_info[0].v_samp_factor;1787v_samp1 = cinfo->comp_info[1].v_samp_factor;1788v_samp2 = cinfo->comp_info[2].v_samp_factor;17891790if (((h_samp1 > h_samp0) && (h_samp2 > h_samp0)) ||1791((v_samp1 > v_samp0) && (v_samp2 > v_samp0)))1792{1793cinfo->jpeg_color_space = JCS_YCCK;1794/* Leave the output space as CMYK */1795}1796}1797RELEASE_ARRAYS(env, data, src->next_input_byte);17981799/* read icc profile data */1800profileData = read_icc_profile(env, cinfo);18011802if ((*env)->ExceptionCheck(env)) {1803return retval;1804}18051806(*env)->CallVoidMethod(env, this,1807JPEGImageReader_setImageDataID,1808cinfo->image_width,1809cinfo->image_height,1810cinfo->jpeg_color_space,1811cinfo->out_color_space,1812cinfo->num_components,1813profileData);1814if ((*env)->ExceptionOccurred(env)1815|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1816cinfo->err->error_exit((j_common_ptr) cinfo);1817}1818if (reset) {1819jpeg_abort_decompress(cinfo);1820}1821RELEASE_ARRAYS(env, data, src->next_input_byte);1822}18231824return retval;1825}182618271828JNIEXPORT void JNICALL1829Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setOutColorSpace1830(JNIEnv *env,1831jobject this,1832jlong ptr,1833jint code) {18341835imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);1836j_decompress_ptr cinfo;18371838if (data == NULL) {1839JNU_ThrowByName(env,1840"java/lang/IllegalStateException",1841"Attempting to use reader after dispose()");1842return;1843}18441845cinfo = (j_decompress_ptr) data->jpegObj;18461847cinfo->out_color_space = code;18481849}18501851JNIEXPORT jboolean JNICALL1852Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage1853(JNIEnv *env,1854jobject this,1855jint imageIndex,1856jlong ptr,1857jbyteArray buffer,1858jint numBands,1859jintArray srcBands,1860jintArray bandSizes,1861jint sourceXStart,1862jint sourceYStart,1863jint sourceWidth,1864jint sourceHeight,1865jint stepX,1866jint stepY,1867jobjectArray qtables,1868jobjectArray DCHuffmanTables,1869jobjectArray ACHuffmanTables,1870jint minProgressivePass, // Counts from 01871jint maxProgressivePass,1872jboolean wantUpdates) {187318741875struct jpeg_source_mgr *src;1876JSAMPROW scanLinePtr = NULL;1877jint bands[MAX_BANDS];1878int i;1879jint *body;1880int scanlineLimit;1881int pixelStride;1882unsigned char *in, *out, *pixelLimit;1883int targetLine;1884int skipLines, linesLeft;1885pixelBufferPtr pb;1886sun_jpeg_error_ptr jerr;1887boolean done;1888boolean progressive = FALSE;1889boolean orderedBands = TRUE;1890imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);1891j_decompress_ptr cinfo;1892size_t numBytes;18931894/* verify the inputs */18951896if (data == NULL) {1897JNU_ThrowByName(env,1898"java/lang/IllegalStateException",1899"Attempting to use reader after dispose()");1900return JNI_FALSE;1901}19021903if ((buffer == NULL) || (srcBands == NULL)) {1904JNU_ThrowNullPointerException(env, 0);1905return JNI_FALSE;1906}19071908cinfo = (j_decompress_ptr) data->jpegObj;19091910if ((numBands < 1) || (numBands > MAX_BANDS) ||1911(sourceXStart < 0) || (sourceXStart >= (jint)cinfo->image_width) ||1912(sourceYStart < 0) || (sourceYStart >= (jint)cinfo->image_height) ||1913(sourceWidth < 1) || (sourceWidth > (jint)cinfo->image_width) ||1914(sourceHeight < 1) || (sourceHeight > (jint)cinfo->image_height) ||1915(stepX < 1) || (stepY < 1) ||1916(minProgressivePass < 0) ||1917(maxProgressivePass < minProgressivePass))1918{1919JNU_ThrowByName(env, "javax/imageio/IIOException",1920"Invalid argument to native readImage");1921return JNI_FALSE;1922}19231924if (stepX > (jint)cinfo->image_width) {1925stepX = cinfo->image_width;1926}1927if (stepY > (jint)cinfo->image_height) {1928stepY = cinfo->image_height;1929}19301931/*1932* First get the source bands array and copy it to our local array1933* so we don't have to worry about pinning and unpinning it again.1934*/19351936body = (*env)->GetIntArrayElements(env, srcBands, NULL);1937if (body == NULL) {1938(*env)->ExceptionClear(env);1939JNU_ThrowByName( env,1940"java/lang/OutOfMemoryError",1941"Initializing Read");1942return JNI_FALSE;1943}19441945for (i = 0; i < numBands; i++) {1946bands[i] = body[i];1947if (orderedBands && (bands[i] != i)) {1948orderedBands = FALSE;1949}1950}19511952(*env)->ReleaseIntArrayElements(env, srcBands, body, JNI_ABORT);19531954#ifdef DEBUG_IIO_JPEG1955printf("---- in reader.read ----\n");1956printf("numBands is %d\n", numBands);1957printf("bands array: ");1958for (i = 0; i < numBands; i++) {1959printf("%d ", bands[i]);1960}1961printf("\n");1962printf("jq table 0 at %p\n",1963cinfo->quant_tbl_ptrs[0]);1964#endif19651966data = (imageIODataPtr) cinfo->client_data;1967src = cinfo->src;19681969/* Set the buffer as our PixelBuffer */1970pb = &data->pixelBuf;19711972if (setPixelBuffer(env, pb, buffer) == NOT_OK) {1973return data->abortFlag; // We already threw an out of memory exception1974}19751976/* Establish the setjmp return context for sun_jpeg_error_exit to use. */1977jerr = (sun_jpeg_error_ptr) cinfo->err;19781979if (setjmp(jerr->setjmp_buffer)) {1980/* If we get here, the JPEG code has signaled an error1981while reading. */1982RELEASE_ARRAYS(env, data, src->next_input_byte);1983if (!(*env)->ExceptionOccurred(env)) {1984char buffer[JMSG_LENGTH_MAX];1985(*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,1986buffer);1987JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);1988}1989if (scanLinePtr != NULL) {1990free(scanLinePtr);1991scanLinePtr = NULL;1992}1993return data->abortFlag;1994}19951996if (GET_ARRAYS(env, data, &src->next_input_byte) == NOT_OK) {1997(*env)->ExceptionClear(env);1998JNU_ThrowByName(env,1999"javax/imageio/IIOException",2000"Array pin failed");2001return data->abortFlag;2002}20032004// If there are no tables in our structure and table arguments aren't2005// NULL, use the table arguments.2006if ((qtables != NULL) && (cinfo->quant_tbl_ptrs[0] == NULL)) {2007(void) setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);2008}20092010if ((DCHuffmanTables != NULL) && (cinfo->dc_huff_tbl_ptrs[0] == NULL)) {2011setHTables(env, (j_common_ptr) cinfo,2012DCHuffmanTables,2013ACHuffmanTables,2014TRUE);2015}20162017progressive = jpeg_has_multiple_scans(cinfo);2018if (progressive) {2019cinfo->buffered_image = TRUE;2020cinfo->input_scan_number = minProgressivePass+1; // Java count from 02021#define MAX_JAVA_INT 2147483647 // XXX Is this defined in JNI somewhere?2022if (maxProgressivePass < MAX_JAVA_INT) {2023maxProgressivePass++; // For testing2024}2025}20262027data->streamBuf.suspendable = FALSE;20282029jpeg_start_decompress(cinfo);20302031if (numBands != cinfo->output_components) {2032RELEASE_ARRAYS(env, data, src->next_input_byte);2033JNU_ThrowByName(env, "javax/imageio/IIOException",2034"Invalid argument to native readImage");2035return data->abortFlag;2036}20372038if (cinfo->output_components <= 0 ||2039cinfo->image_width > (0xffffffffu / (unsigned int)cinfo->output_components))2040{2041RELEASE_ARRAYS(env, data, src->next_input_byte);2042JNU_ThrowByName(env, "javax/imageio/IIOException",2043"Invalid number of output components");2044return data->abortFlag;2045}20462047// Allocate a 1-scanline buffer2048scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->output_components);2049if (scanLinePtr == NULL) {2050RELEASE_ARRAYS(env, data, src->next_input_byte);2051JNU_ThrowByName( env,2052"java/lang/OutOfMemoryError",2053"Reading JPEG Stream");2054return data->abortFlag;2055}20562057// loop over progressive passes2058done = FALSE;2059while (!done) {2060if (progressive) {2061// initialize the next pass. Note that this skips up to2062// the first interesting pass.2063jpeg_start_output(cinfo, cinfo->input_scan_number);2064if (wantUpdates) {2065RELEASE_ARRAYS(env, data, src->next_input_byte);2066(*env)->CallVoidMethod(env, this,2067JPEGImageReader_passStartedID,2068cinfo->input_scan_number-1);2069if ((*env)->ExceptionOccurred(env)2070|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2071cinfo->err->error_exit((j_common_ptr) cinfo);2072}2073}2074} else if (wantUpdates) {2075RELEASE_ARRAYS(env, data, src->next_input_byte);2076(*env)->CallVoidMethod(env, this,2077JPEGImageReader_passStartedID,20780);2079if ((*env)->ExceptionOccurred(env)2080|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2081cinfo->err->error_exit((j_common_ptr) cinfo);2082}2083}20842085// Skip until the first interesting line2086while ((data->abortFlag == JNI_FALSE)2087&& ((jint)cinfo->output_scanline < sourceYStart)) {2088jpeg_read_scanlines(cinfo, &scanLinePtr, 1);2089}20902091scanlineLimit = sourceYStart+sourceHeight;2092pixelLimit = scanLinePtr2093+(sourceXStart+sourceWidth)*cinfo->output_components;20942095pixelStride = stepX*cinfo->output_components;2096targetLine = 0;20972098while ((data->abortFlag == JNI_FALSE)2099&& ((jint)cinfo->output_scanline < scanlineLimit)) {21002101jpeg_read_scanlines(cinfo, &scanLinePtr, 1);21022103// Now mangle it into our buffer2104out = data->pixelBuf.buf.bp;21052106if (orderedBands && (pixelStride == numBands)) {2107// Optimization: The component bands are ordered sequentially,2108// so we can simply use memcpy() to copy the intermediate2109// scanline buffer into the raster.2110in = scanLinePtr + (sourceXStart * cinfo->output_components);2111if (pixelLimit > in) {2112numBytes = pixelLimit - in;2113if (numBytes > data->pixelBuf.byteBufferLength) {2114numBytes = data->pixelBuf.byteBufferLength;2115}2116memcpy(out, in, numBytes);2117}2118} else {2119numBytes = numBands;2120for (in = scanLinePtr+sourceXStart*cinfo->output_components;2121in < pixelLimit &&2122numBytes <= data->pixelBuf.byteBufferLength;2123in += pixelStride) {2124for (i = 0; i < numBands; i++) {2125*out++ = *(in+bands[i]);2126}2127numBytes += numBands;2128}2129}21302131// And call it back to Java2132RELEASE_ARRAYS(env, data, src->next_input_byte);2133(*env)->CallVoidMethod(env,2134this,2135JPEGImageReader_acceptPixelsID,2136targetLine++,2137progressive);21382139if ((*env)->ExceptionOccurred(env)2140|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2141cinfo->err->error_exit((j_common_ptr) cinfo);2142}21432144// And skip over uninteresting lines to the next subsampled line2145// Ensure we don't go past the end of the image21462147// Lines to skip based on subsampling2148skipLines = stepY - 1;2149// Lines left in the image2150linesLeft = scanlineLimit - cinfo->output_scanline;2151// Take the minimum2152if (skipLines > linesLeft) {2153skipLines = linesLeft;2154}2155for(i = 0; i < skipLines; i++) {2156jpeg_read_scanlines(cinfo, &scanLinePtr, 1);2157}2158}2159if (progressive) {2160jpeg_finish_output(cinfo); // Increments pass counter2161// Call Java to notify pass complete2162if (jpeg_input_complete(cinfo)2163|| (cinfo->input_scan_number > maxProgressivePass)) {2164done = TRUE;2165}2166} else {2167done = TRUE;2168}2169if (wantUpdates) {2170RELEASE_ARRAYS(env, data, src->next_input_byte);2171(*env)->CallVoidMethod(env, this,2172JPEGImageReader_passCompleteID);2173if ((*env)->ExceptionOccurred(env)2174|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2175cinfo->err->error_exit((j_common_ptr) cinfo);2176}2177}21782179}2180/*2181* We are done, but we might not have read all the lines, or all2182* the passes, so use jpeg_abort instead of jpeg_finish_decompress.2183*/2184if ((cinfo->output_scanline != cinfo->output_height) ||2185data->abortFlag == JNI_TRUE)2186{2187jpeg_abort_decompress(cinfo);2188} else if ((!jpeg_input_complete(cinfo)) &&2189(progressive &&2190(cinfo->input_scan_number > maxProgressivePass))) {2191/* We haven't reached EOI, but we need to skip to there */2192(*cinfo->src->term_source) (cinfo);2193/* We can use jpeg_abort to release memory and reset global_state */2194jpeg_abort((j_common_ptr) cinfo);2195(*env)->CallVoidMethod(env,2196this,2197JPEGImageReader_skipPastImageID,2198imageIndex);2199} else {2200jpeg_finish_decompress(cinfo);2201}22022203free(scanLinePtr);22042205RELEASE_ARRAYS(env, data, src->next_input_byte);22062207return data->abortFlag;2208}22092210JNIEXPORT void JNICALL2211Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_clearNativeReadAbortFlag2212(JNIEnv *env,2213jobject this,2214jlong ptr) {22152216imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);22172218if (data == NULL) {2219JNU_ThrowByName(env,2220"java/lang/IllegalStateException",2221"Attempting to use reader after dispose()");2222return;2223}22242225data->abortFlag = JNI_FALSE;22262227}22282229JNIEXPORT void JNICALL2230Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_abortRead2231(JNIEnv *env,2232jobject this,2233jlong ptr) {22342235imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);22362237if (data == NULL) {2238JNU_ThrowByName(env,2239"java/lang/IllegalStateException",2240"Attempting to use reader after dispose()");2241return;2242}22432244imageio_abort(env, this, data);22452246}22472248JNIEXPORT void JNICALL2249Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetLibraryState2250(JNIEnv *env,2251jobject this,2252jlong ptr) {2253imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2254j_decompress_ptr cinfo;22552256if (data == NULL) {2257JNU_ThrowByName(env,2258"java/lang/IllegalStateException",2259"Attempting to use reader after dispose()");2260return;2261}22622263cinfo = (j_decompress_ptr) data->jpegObj;22642265jpeg_abort_decompress(cinfo);2266}226722682269JNIEXPORT void JNICALL2270Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetReader2271(JNIEnv *env,2272jobject this,2273jlong ptr) {22742275imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2276j_decompress_ptr cinfo;2277sun_jpeg_error_ptr jerr;22782279if (data == NULL) {2280JNU_ThrowByName(env,2281"java/lang/IllegalStateException",2282"Attempting to use reader after dispose()");2283return;2284}22852286cinfo = (j_decompress_ptr) data->jpegObj;22872288jerr = (sun_jpeg_error_ptr) cinfo->err;22892290imageio_reset(env, (j_common_ptr) cinfo, data);22912292/*2293* The tables have not been reset, and there is no way to do so2294* in IJG without leaking memory. The only situation in which2295* this will cause a problem is if an image-only stream is read2296* with this object without initializing the correct tables first.2297* This situation, which should cause an error, might work but2298* produce garbage instead. If the huffman tables are wrong,2299* it will fail during the decode. If the q tables are wrong, it2300* will look strange. This is very unlikely, so don't worry about2301* it. To be really robust, we would keep a flag for table state2302* and consult it to catch exceptional situations.2303*/23042305/* above does not clean up the source, so we have to */23062307/*2308We need to explicitly initialize exception handler or we may2309longjump to random address from the term_source()2310*/23112312if (setjmp(jerr->setjmp_buffer)) {23132314/*2315We may get IOException from pushBack() here.23162317However it could be legal if original input stream was closed2318earlier (for example because network connection was closed).2319Unfortunately, image inputstream API has no way to check whether2320stream is already closed or IOException was thrown because of some2321other IO problem,2322And we can not avoid call to pushBack() on closed stream for the2323same reason.23242325So, for now we will silently eat this exception.23262327NB: this may be changed in future when ImageInputStream API will2328become more flexible.2329*/23302331if ((*env)->ExceptionOccurred(env)) {2332(*env)->ExceptionClear(env);2333}2334} else {2335cinfo->src->term_source(cinfo);2336}23372338cinfo->src->bytes_in_buffer = 0;2339cinfo->src->next_input_byte = NULL;2340}23412342JNIEXPORT void JNICALL2343Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_disposeReader2344(JNIEnv *env,2345jclass reader,2346jlong ptr) {23472348imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2349j_common_ptr info = destroyImageioData(env, data);23502351imageio_dispose(info);2352}23532354/********************** end of Reader *************************/23552356/********************** Writer Support ************************/23572358/********************** Destination Manager *******************/23592360METHODDEF(void)2361/*2362* Initialize destination --- called by jpeg_start_compress2363* before any data is actually written. The data arrays2364* must be pinned before this is called.2365*/2366imageio_init_destination (j_compress_ptr cinfo)2367{2368struct jpeg_destination_mgr *dest = cinfo->dest;2369imageIODataPtr data = (imageIODataPtr) cinfo->client_data;2370streamBufferPtr sb = &data->streamBuf;2371JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);23722373if (sb->buf == NULL) {2374// We forgot to pin the array2375(*env)->FatalError(env, "Output buffer not pinned!");2376}23772378dest->next_output_byte = sb->buf;2379dest->free_in_buffer = sb->bufferLength;2380}23812382/*2383* Empty the output buffer --- called whenever buffer fills up.2384*2385* This routine writes the entire output buffer2386* (ignoring the current state of next_output_byte & free_in_buffer),2387* resets the pointer & count to the start of the buffer, and returns TRUE2388* indicating that the buffer has been dumped.2389*/23902391METHODDEF(boolean)2392imageio_empty_output_buffer (j_compress_ptr cinfo)2393{2394struct jpeg_destination_mgr *dest = cinfo->dest;2395imageIODataPtr data = (imageIODataPtr) cinfo->client_data;2396streamBufferPtr sb = &data->streamBuf;2397JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);2398jobject output = NULL;23992400RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));24012402GET_IO_REF(output);24032404(*env)->CallVoidMethod(env,2405output,2406JPEGImageWriter_writeOutputDataID,2407sb->hstreamBuffer,24080,2409sb->bufferLength);2410if ((*env)->ExceptionOccurred(env)2411|| !GET_ARRAYS(env, data,2412(const JOCTET **)(&dest->next_output_byte))) {2413cinfo->err->error_exit((j_common_ptr) cinfo);2414}24152416dest->next_output_byte = sb->buf;2417dest->free_in_buffer = sb->bufferLength;24182419return TRUE;2420}24212422/*2423* After all of the data has been encoded there may still be some2424* more left over in some of the working buffers. Now is the2425* time to clear them out.2426*/2427METHODDEF(void)2428imageio_term_destination (j_compress_ptr cinfo)2429{2430struct jpeg_destination_mgr *dest = cinfo->dest;2431imageIODataPtr data = (imageIODataPtr) cinfo->client_data;2432streamBufferPtr sb = &data->streamBuf;2433JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);24342435/* find out how much needs to be written */2436/* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */2437jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer);24382439if (datacount != 0) {2440jobject output = NULL;24412442RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));24432444GET_IO_REF(output);24452446(*env)->CallVoidMethod(env,2447output,2448JPEGImageWriter_writeOutputDataID,2449sb->hstreamBuffer,24500,2451datacount);24522453if ((*env)->ExceptionOccurred(env)2454|| !GET_ARRAYS(env, data,2455(const JOCTET **)(&dest->next_output_byte))) {2456cinfo->err->error_exit((j_common_ptr) cinfo);2457}2458}24592460dest->next_output_byte = NULL;2461dest->free_in_buffer = 0;24622463}24642465/*2466* Flush the destination buffer. This is not called by the library,2467* but by our code below. This is the simplest implementation, though2468* certainly not the most efficient.2469*/2470METHODDEF(void)2471imageio_flush_destination(j_compress_ptr cinfo)2472{2473imageio_term_destination(cinfo);2474imageio_init_destination(cinfo);2475}24762477/********************** end of destination manager ************/24782479/********************** Writer JNI calls **********************/248024812482JNIEXPORT void JNICALL2483Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initWriterIDs2484(JNIEnv *env,2485jclass cls,2486jclass qTableClass,2487jclass huffClass) {24882489CHECK_NULL(JPEGImageWriter_writeOutputDataID = (*env)->GetMethodID(env,2490cls,2491"writeOutputData",2492"([BII)V"));2493CHECK_NULL(JPEGImageWriter_warningOccurredID = (*env)->GetMethodID(env,2494cls,2495"warningOccurred",2496"(I)V"));2497CHECK_NULL(JPEGImageWriter_warningWithMessageID =2498(*env)->GetMethodID(env,2499cls,2500"warningWithMessage",2501"(Ljava/lang/String;)V"));2502CHECK_NULL(JPEGImageWriter_writeMetadataID = (*env)->GetMethodID(env,2503cls,2504"writeMetadata",2505"()V"));2506CHECK_NULL(JPEGImageWriter_grabPixelsID = (*env)->GetMethodID(env,2507cls,2508"grabPixels",2509"(I)V"));2510CHECK_NULL(JPEGQTable_tableID = (*env)->GetFieldID(env,2511qTableClass,2512"qTable",2513"[I"));2514CHECK_NULL(JPEGHuffmanTable_lengthsID = (*env)->GetFieldID(env,2515huffClass,2516"lengths",2517"[S"));2518CHECK_NULL(JPEGHuffmanTable_valuesID = (*env)->GetFieldID(env,2519huffClass,2520"values",2521"[S"));2522}25232524JNIEXPORT jlong JNICALL2525Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initJPEGImageWriter2526(JNIEnv *env,2527jobject this) {25282529imageIODataPtr ret;2530struct sun_jpeg_error_mgr *jerr;2531struct jpeg_destination_mgr *dest;25322533/* This struct contains the JPEG compression parameters and pointers to2534* working space (which is allocated as needed by the JPEG library).2535*/2536struct jpeg_compress_struct *cinfo =2537malloc(sizeof(struct jpeg_compress_struct));2538if (cinfo == NULL) {2539JNU_ThrowByName( env,2540"java/lang/OutOfMemoryError",2541"Initializing Writer");2542return 0;2543}25442545/* We use our private extension JPEG error handler.2546*/2547jerr = malloc (sizeof(struct sun_jpeg_error_mgr));2548if (jerr == NULL) {2549JNU_ThrowByName( env,2550"java/lang/OutOfMemoryError",2551"Initializing Writer");2552free(cinfo);2553return 0;2554}25552556/* We set up the normal JPEG error routines, then override error_exit. */2557cinfo->err = jpeg_std_error(&(jerr->pub));2558jerr->pub.error_exit = sun_jpeg_error_exit;2559/* We need to setup our own print routines */2560jerr->pub.output_message = sun_jpeg_output_message;2561/* Now we can setjmp before every call to the library */25622563/* Establish the setjmp return context for sun_jpeg_error_exit to use. */2564if (setjmp(jerr->setjmp_buffer)) {2565/* If we get here, the JPEG code has signaled an error. */2566char buffer[JMSG_LENGTH_MAX];2567(*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,2568buffer);2569JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);2570return 0;2571}25722573/* Perform library initialization */2574jpeg_create_compress(cinfo);25752576/* Now set up the destination */2577dest = malloc(sizeof(struct jpeg_destination_mgr));2578if (dest == NULL) {2579JNU_ThrowByName( env,2580"java/lang/OutOfMemoryError",2581"Initializing Writer");2582imageio_dispose((j_common_ptr)cinfo);2583return 0;2584}25852586dest->init_destination = imageio_init_destination;2587dest->empty_output_buffer = imageio_empty_output_buffer;2588dest->term_destination = imageio_term_destination;2589dest->next_output_byte = NULL;2590dest->free_in_buffer = 0;25912592cinfo->dest = dest;25932594/* set up the association to persist for future calls */2595ret = initImageioData(env, (j_common_ptr) cinfo, this);2596if (ret == NULL) {2597(*env)->ExceptionClear(env);2598JNU_ThrowByName( env,2599"java/lang/OutOfMemoryError",2600"Initializing Writer");2601imageio_dispose((j_common_ptr)cinfo);2602return 0;2603}2604return ptr_to_jlong(ret);2605}26062607JNIEXPORT void JNICALL2608Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_setDest2609(JNIEnv *env,2610jobject this,2611jlong ptr) {26122613imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2614j_compress_ptr cinfo;26152616if (data == NULL) {2617JNU_ThrowByName(env,2618"java/lang/IllegalStateException",2619"Attempting to use writer after dispose()");2620return;2621}26222623cinfo = (j_compress_ptr) data->jpegObj;26242625imageio_set_stream(env, data->jpegObj, data, this);262626272628// Don't call the init method, as that depends on pinned arrays2629cinfo->dest->next_output_byte = NULL;2630cinfo->dest->free_in_buffer = 0;2631}26322633JNIEXPORT void JNICALL2634Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables2635(JNIEnv *env,2636jobject this,2637jlong ptr,2638jobjectArray qtables,2639jobjectArray DCHuffmanTables,2640jobjectArray ACHuffmanTables) {26412642struct jpeg_destination_mgr *dest;2643sun_jpeg_error_ptr jerr;2644imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2645j_compress_ptr cinfo;26462647if (data == NULL) {2648JNU_ThrowByName(env,2649"java/lang/IllegalStateException",2650"Attempting to use writer after dispose()");2651return;2652}26532654cinfo = (j_compress_ptr) data->jpegObj;2655dest = cinfo->dest;26562657/* Establish the setjmp return context for sun_jpeg_error_exit to use. */2658jerr = (sun_jpeg_error_ptr) cinfo->err;26592660if (setjmp(jerr->setjmp_buffer)) {2661/* If we get here, the JPEG code has signaled an error2662while writing. */2663RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));2664if (!(*env)->ExceptionOccurred(env)) {2665char buffer[JMSG_LENGTH_MAX];2666(*cinfo->err->format_message) ((j_common_ptr) cinfo,2667buffer);2668JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);2669}2670return;2671}26722673if (GET_ARRAYS(env, data,2674(const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {2675(*env)->ExceptionClear(env);2676JNU_ThrowByName(env,2677"javax/imageio/IIOException",2678"Array pin failed");2679return;2680}26812682jpeg_suppress_tables(cinfo, TRUE); // Suppress writing of any current26832684data->streamBuf.suspendable = FALSE;2685if (qtables != NULL) {2686#ifdef DEBUG_IIO_JPEG2687printf("in writeTables: qtables not NULL\n");2688#endif2689setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);2690}26912692if (DCHuffmanTables != NULL) {2693setHTables(env, (j_common_ptr) cinfo,2694DCHuffmanTables, ACHuffmanTables, TRUE);2695}26962697jpeg_write_tables(cinfo); // Flushes the buffer for you2698RELEASE_ARRAYS(env, data, NULL);2699}27002701static void freeArray(UINT8** arr, jint size) {2702int i;2703if (arr != NULL) {2704for (i = 0; i < size; i++) {2705if (arr[i] != NULL) {2706free(arr[i]);2707}2708}2709free(arr);2710}2711}27122713JNIEXPORT jboolean JNICALL2714Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage2715(JNIEnv *env,2716jobject this,2717jlong ptr,2718jbyteArray buffer,2719jint inCs, jint outCs,2720jint numBands,2721jintArray bandSizes,2722jint srcWidth,2723jint destWidth, jint destHeight,2724jint stepX, jint stepY,2725jobjectArray qtables,2726jboolean writeDQT,2727jobjectArray DCHuffmanTables,2728jobjectArray ACHuffmanTables,2729jboolean writeDHT,2730jboolean optimize,2731jboolean progressive,2732jint numScans,2733jintArray scanInfo,2734jintArray componentIds,2735jintArray HsamplingFactors,2736jintArray VsamplingFactors,2737jintArray QtableSelectors,2738jboolean haveMetadata,2739jint restartInterval) {27402741struct jpeg_destination_mgr *dest;2742JSAMPROW scanLinePtr;2743int i, j;2744int pixelStride;2745unsigned char *in, *out, *pixelLimit, *scanLineLimit;2746unsigned int scanLineSize, pixelBufferSize;2747int targetLine;2748pixelBufferPtr pb;2749sun_jpeg_error_ptr jerr;2750jint *ids, *hfactors, *vfactors, *qsels;2751jsize qlen, hlen;2752int *scanptr;2753jint *scanData;2754jint *bandSize;2755int maxBandValue, halfMaxBandValue;2756imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2757j_compress_ptr cinfo;2758UINT8** scale = NULL;2759boolean success = TRUE;276027612762/* verify the inputs */27632764if (data == NULL) {2765JNU_ThrowByName(env,2766"java/lang/IllegalStateException",2767"Attempting to use writer after dispose()");2768return JNI_FALSE;2769}27702771if ((buffer == NULL) ||2772(qtables == NULL) ||2773// H tables can be null if optimizing2774(componentIds == NULL) ||2775(HsamplingFactors == NULL) || (VsamplingFactors == NULL) ||2776(QtableSelectors == NULL) ||2777((numScans != 0) && (scanInfo != NULL))) {27782779JNU_ThrowNullPointerException(env, 0);2780return JNI_FALSE;27812782}27832784scanLineSize = destWidth * numBands;2785if ((inCs < 0) || (inCs > JCS_YCCK) ||2786(outCs < 0) || (outCs > JCS_YCCK) ||2787(numBands < 1) || (numBands > MAX_BANDS) ||2788(srcWidth < 0) ||2789(destWidth < 0) || (destWidth > srcWidth) ||2790(destHeight < 0) ||2791(stepX < 0) || (stepY < 0) ||2792((INT_MAX / numBands) < destWidth)) /* destWidth causes an integer overflow */2793{2794JNU_ThrowByName(env, "javax/imageio/IIOException",2795"Invalid argument to native writeImage");2796return JNI_FALSE;2797}27982799if (stepX > srcWidth) {2800stepX = srcWidth;2801}28022803bandSize = (*env)->GetIntArrayElements(env, bandSizes, NULL);2804CHECK_NULL_RETURN(bandSize, JNI_FALSE);28052806for (i = 0; i < numBands; i++) {2807if (bandSize[i] <= 0 || bandSize[i] > JPEG_BAND_SIZE) {2808(*env)->ReleaseIntArrayElements(env, bandSizes,2809bandSize, JNI_ABORT);2810JNU_ThrowByName(env, "javax/imageio/IIOException", "Invalid Image");2811return JNI_FALSE;2812}2813}28142815for (i = 0; i < numBands; i++) {2816if (bandSize[i] != JPEG_BAND_SIZE) {2817if (scale == NULL) {2818scale = (UINT8**) calloc(numBands, sizeof(UINT8*));28192820if (scale == NULL) {2821(*env)->ReleaseIntArrayElements(env, bandSizes,2822bandSize, JNI_ABORT);2823JNU_ThrowByName( env, "java/lang/OutOfMemoryError",2824"Writing JPEG Stream");2825return JNI_FALSE;2826}2827}28282829maxBandValue = (1 << bandSize[i]) - 1;28302831scale[i] = (UINT8*) malloc((maxBandValue + 1) * sizeof(UINT8));28322833if (scale[i] == NULL) {2834// Cleanup before throwing an out of memory exception2835for (j = 0; j < i; j++) {2836free(scale[j]);2837}2838free(scale);2839(*env)->ReleaseIntArrayElements(env, bandSizes,2840bandSize, JNI_ABORT);2841JNU_ThrowByName( env, "java/lang/OutOfMemoryError",2842"Writing JPEG Stream");2843return JNI_FALSE;2844}28452846halfMaxBandValue = maxBandValue >> 1;28472848for (j = 0; j <= maxBandValue; j++) {2849scale[i][j] = (UINT8)2850((j*MAX_JPEG_BAND_VALUE + halfMaxBandValue)/maxBandValue);2851}2852}2853}28542855(*env)->ReleaseIntArrayElements(env, bandSizes,2856bandSize, JNI_ABORT);28572858cinfo = (j_compress_ptr) data->jpegObj;2859dest = cinfo->dest;28602861/* Set the buffer as our PixelBuffer */2862pb = &data->pixelBuf;28632864if (setPixelBuffer(env, pb, buffer) == NOT_OK) {2865freeArray(scale, numBands);2866return data->abortFlag; // We already threw an out of memory exception2867}28682869// Allocate a 1-scanline buffer2870scanLinePtr = (JSAMPROW)malloc(scanLineSize);2871if (scanLinePtr == NULL) {2872freeArray(scale, numBands);2873JNU_ThrowByName( env,2874"java/lang/OutOfMemoryError",2875"Writing JPEG Stream");2876return data->abortFlag;2877}2878scanLineLimit = scanLinePtr + scanLineSize;28792880/* Establish the setjmp return context for sun_jpeg_error_exit to use. */2881jerr = (sun_jpeg_error_ptr) cinfo->err;28822883if (setjmp(jerr->setjmp_buffer)) {2884/* If we get here, the JPEG code has signaled an error2885while writing. */2886RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));2887if (!(*env)->ExceptionOccurred(env)) {2888char buffer[JMSG_LENGTH_MAX];2889(*cinfo->err->format_message) ((j_common_ptr) cinfo,2890buffer);2891JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);2892}28932894freeArray(scale, numBands);2895free(scanLinePtr);2896return data->abortFlag;2897}28982899// set up parameters2900cinfo->image_width = destWidth;2901cinfo->image_height = destHeight;2902cinfo->input_components = numBands;2903cinfo->in_color_space = inCs;29042905jpeg_set_defaults(cinfo);29062907jpeg_set_colorspace(cinfo, outCs);29082909cinfo->optimize_coding = optimize;29102911cinfo->write_JFIF_header = FALSE;2912cinfo->write_Adobe_marker = FALSE;2913// copy componentIds2914ids = (*env)->GetIntArrayElements(env, componentIds, NULL);2915hfactors = (*env)->GetIntArrayElements(env, HsamplingFactors, NULL);2916vfactors = (*env)->GetIntArrayElements(env, VsamplingFactors, NULL);2917qsels = (*env)->GetIntArrayElements(env, QtableSelectors, NULL);29182919if (ids && hfactors && vfactors && qsels) {2920for (i = 0; i < numBands; i++) {2921cinfo->comp_info[i].component_id = ids[i];2922cinfo->comp_info[i].h_samp_factor = hfactors[i];2923cinfo->comp_info[i].v_samp_factor = vfactors[i];2924cinfo->comp_info[i].quant_tbl_no = qsels[i];2925}2926} else {2927success = FALSE;2928}29292930if (ids) {2931(*env)->ReleaseIntArrayElements(env, componentIds, ids, JNI_ABORT);2932}2933if (hfactors) {2934(*env)->ReleaseIntArrayElements(env, HsamplingFactors, hfactors, JNI_ABORT);2935}2936if (vfactors) {2937(*env)->ReleaseIntArrayElements(env, VsamplingFactors, vfactors, JNI_ABORT);2938}2939if (qsels) {2940(*env)->ReleaseIntArrayElements(env, QtableSelectors, qsels, JNI_ABORT);2941}2942if (!success) {2943freeArray(scale, numBands);2944free(scanLinePtr);2945return data->abortFlag;2946}29472948jpeg_suppress_tables(cinfo, TRUE); // Disable writing any current29492950qlen = setQTables(env, (j_common_ptr) cinfo, qtables, writeDQT);29512952if (!optimize) {2953// Set the h tables2954hlen = setHTables(env,2955(j_common_ptr) cinfo,2956DCHuffmanTables,2957ACHuffmanTables,2958writeDHT);2959}29602961if (GET_ARRAYS(env, data,2962(const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {2963(*env)->ExceptionClear(env);2964freeArray(scale, numBands);2965free(scanLinePtr);2966JNU_ThrowByName(env,2967"javax/imageio/IIOException",2968"Array pin failed");2969return data->abortFlag;2970}29712972data->streamBuf.suspendable = FALSE;29732974if (progressive) {2975if (numScans == 0) { // then use default scans2976jpeg_simple_progression(cinfo);2977} else {2978cinfo->num_scans = numScans;2979// Copy the scanInfo to a local array2980// The following is copied from jpeg_simple_progression:2981/* Allocate space for script.2982* We need to put it in the permanent pool in case the application performs2983* multiple compressions without changing the settings. To avoid a memory2984* leak if jpeg_simple_progression is called repeatedly for the same JPEG2985* object, we try to re-use previously allocated space, and we allocate2986* enough space to handle YCbCr even if initially asked for grayscale.2987*/2988if (cinfo->script_space == NULL2989|| cinfo->script_space_size < numScans) {2990cinfo->script_space_size = MAX(numScans, 10);2991cinfo->script_space = (jpeg_scan_info *)2992(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,2993JPOOL_PERMANENT,2994cinfo->script_space_size2995* sizeof(jpeg_scan_info));2996}2997cinfo->scan_info = cinfo->script_space;2998scanptr = (int *) cinfo->script_space;2999scanData = (*env)->GetIntArrayElements(env, scanInfo, NULL);3000if (scanData == NULL) {3001RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));3002freeArray(scale, numBands);3003free(scanLinePtr);3004return data->abortFlag;3005}3006// number of jints per scan is 93007// We avoid a memcpy to handle different size ints3008for (i = 0; i < numScans*9; i++) {3009scanptr[i] = scanData[i];3010}3011(*env)->ReleaseIntArrayElements(env, scanInfo,3012scanData, JNI_ABORT);30133014}3015}30163017cinfo->restart_interval = restartInterval;30183019#ifdef DEBUG_IIO_JPEG3020printf("writer setup complete, starting compressor\n");3021#endif30223023// start the compressor; tables must already be set3024jpeg_start_compress(cinfo, FALSE); // Leaves sent_table alone30253026if (haveMetadata) {3027// Flush the buffer3028imageio_flush_destination(cinfo);3029// Call Java to write the metadata3030RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));3031(*env)->CallVoidMethod(env,3032this,3033JPEGImageWriter_writeMetadataID);3034if ((*env)->ExceptionOccurred(env)3035|| !GET_ARRAYS(env, data,3036(const JOCTET **)(&dest->next_output_byte))) {3037cinfo->err->error_exit((j_common_ptr) cinfo);3038}3039}30403041targetLine = 0;3042pixelBufferSize = srcWidth * numBands;3043pixelStride = numBands * stepX;30443045// for each line in destHeight3046while ((data->abortFlag == JNI_FALSE)3047&& (cinfo->next_scanline < cinfo->image_height)) {3048// get the line from Java3049RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));3050(*env)->CallVoidMethod(env,3051this,3052JPEGImageWriter_grabPixelsID,3053targetLine);3054if ((*env)->ExceptionOccurred(env)3055|| !GET_ARRAYS(env, data,3056(const JOCTET **)(&dest->next_output_byte))) {3057cinfo->err->error_exit((j_common_ptr) cinfo);3058}30593060// subsample it into our buffer30613062in = data->pixelBuf.buf.bp;3063out = scanLinePtr;3064pixelLimit = in + ((pixelBufferSize > data->pixelBuf.byteBufferLength) ?3065data->pixelBuf.byteBufferLength : pixelBufferSize);3066for (; (in < pixelLimit) && (out < scanLineLimit); in += pixelStride) {3067for (i = 0; i < numBands; i++) {3068if (scale !=NULL && scale[i] != NULL) {3069*out++ = scale[i][*(in+i)];3070#ifdef DEBUG_IIO_JPEG3071if (in == data->pixelBuf.buf.bp){ // Just the first pixel3072printf("in %d -> out %d, ", *(in+i), *(out-i-1));3073}3074#endif30753076#ifdef DEBUG_IIO_JPEG3077if (in == data->pixelBuf.buf.bp){ // Just the first pixel3078printf("\n");3079}3080#endif3081} else {3082*out++ = *(in+i);3083}3084}3085}3086// write it out3087jpeg_write_scanlines(cinfo, (JSAMPARRAY)&scanLinePtr, 1);3088targetLine += stepY;3089}30903091/*3092* We are done, but we might not have done all the lines,3093* so use jpeg_abort instead of jpeg_finish_compress.3094*/3095if (cinfo->next_scanline == cinfo->image_height) {3096jpeg_finish_compress(cinfo); // Flushes buffer with term_dest3097} else {3098jpeg_abort((j_common_ptr)cinfo);3099}31003101freeArray(scale, numBands);3102free(scanLinePtr);3103RELEASE_ARRAYS(env, data, NULL);3104return data->abortFlag;3105}31063107JNIEXPORT void JNICALL3108Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_abortWrite3109(JNIEnv *env,3110jobject this,3111jlong ptr) {31123113imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);31143115if (data == NULL) {3116JNU_ThrowByName(env,3117"java/lang/IllegalStateException",3118"Attempting to use writer after dispose()");3119return;3120}31213122imageio_abort(env, this, data);3123}31243125JNIEXPORT void JNICALL3126Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_resetWriter3127(JNIEnv *env,3128jobject this,3129jlong ptr) {3130imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);3131j_compress_ptr cinfo;31323133if (data == NULL) {3134JNU_ThrowByName(env,3135"java/lang/IllegalStateException",3136"Attempting to use writer after dispose()");3137return;3138}31393140cinfo = (j_compress_ptr) data->jpegObj;31413142imageio_reset(env, (j_common_ptr) cinfo, data);31433144/*3145* The tables have not been reset, and there is no way to do so3146* in IJG without leaking memory. The only situation in which3147* this will cause a problem is if an image-only stream is written3148* with this object without initializing the correct tables first,3149* which should not be possible.3150*/31513152cinfo->dest->next_output_byte = NULL;3153cinfo->dest->free_in_buffer = 0;3154}31553156JNIEXPORT void JNICALL3157Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_disposeWriter3158(JNIEnv *env,3159jclass writer,3160jlong ptr) {31613162imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);3163j_common_ptr info = destroyImageioData(env, data);31643165imageio_dispose(info);3166}316731683169