Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/awt/image/jpeg/imageioJPEG.c
38918 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 *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(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(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 = 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(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 = 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(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(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;1764#ifdef YCCALPHA1765case JCS_YCC:1766cinfo->out_color_space = JCS_YCC;1767break;1768#endif1769case JCS_YCCK:1770if ((cinfo->saw_Adobe_marker) && (cinfo->Adobe_transform != 2)) {1771/*1772* IJG guesses this is YCCK and emits a warning1773* We would rather not guess. Then the user knows1774* To read this as a Raster if at all1775*/1776cinfo->jpeg_color_space = JCS_UNKNOWN;1777cinfo->out_color_space = JCS_UNKNOWN;1778}1779break;1780case JCS_CMYK:1781/*1782* IJG assumes all unidentified 4-channels are CMYK.1783* We assume that only if the second two channels are1784* not subsampled (either horizontally or vertically).1785* If they are, we assume YCCK.1786*/1787h_samp0 = cinfo->comp_info[0].h_samp_factor;1788h_samp1 = cinfo->comp_info[1].h_samp_factor;1789h_samp2 = cinfo->comp_info[2].h_samp_factor;17901791v_samp0 = cinfo->comp_info[0].v_samp_factor;1792v_samp1 = cinfo->comp_info[1].v_samp_factor;1793v_samp2 = cinfo->comp_info[2].v_samp_factor;17941795if ((h_samp1 > h_samp0) && (h_samp2 > h_samp0) ||1796(v_samp1 > v_samp0) && (v_samp2 > v_samp0))1797{1798cinfo->jpeg_color_space = JCS_YCCK;1799/* Leave the output space as CMYK */1800}1801}1802RELEASE_ARRAYS(env, data, src->next_input_byte);18031804/* read icc profile data */1805profileData = read_icc_profile(env, cinfo);18061807if ((*env)->ExceptionCheck(env)) {1808return retval;1809}18101811(*env)->CallVoidMethod(env, this,1812JPEGImageReader_setImageDataID,1813cinfo->image_width,1814cinfo->image_height,1815cinfo->jpeg_color_space,1816cinfo->out_color_space,1817cinfo->num_components,1818profileData);1819if ((*env)->ExceptionOccurred(env)1820|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {1821cinfo->err->error_exit((j_common_ptr) cinfo);1822}1823if (reset) {1824jpeg_abort_decompress(cinfo);1825}1826RELEASE_ARRAYS(env, data, src->next_input_byte);1827}18281829return retval;1830}183118321833JNIEXPORT void JNICALL1834Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setOutColorSpace1835(JNIEnv *env,1836jobject this,1837jlong ptr,1838jint code) {18391840imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);1841j_decompress_ptr cinfo;18421843if (data == NULL) {1844JNU_ThrowByName(env,1845"java/lang/IllegalStateException",1846"Attempting to use reader after dispose()");1847return;1848}18491850cinfo = (j_decompress_ptr) data->jpegObj;18511852cinfo->out_color_space = code;18531854}18551856JNIEXPORT jboolean JNICALL1857Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage1858(JNIEnv *env,1859jobject this,1860jint imageIndex,1861jlong ptr,1862jbyteArray buffer,1863jint numBands,1864jintArray srcBands,1865jintArray bandSizes,1866jint sourceXStart,1867jint sourceYStart,1868jint sourceWidth,1869jint sourceHeight,1870jint stepX,1871jint stepY,1872jobjectArray qtables,1873jobjectArray DCHuffmanTables,1874jobjectArray ACHuffmanTables,1875jint minProgressivePass, // Counts from 01876jint maxProgressivePass,1877jboolean wantUpdates) {187818791880struct jpeg_source_mgr *src;1881JSAMPROW scanLinePtr = NULL;1882jint bands[MAX_BANDS];1883int i;1884jint *body;1885int scanlineLimit;1886int pixelStride;1887unsigned char *in, *out, *pixelLimit;1888int targetLine;1889int skipLines, linesLeft;1890pixelBufferPtr pb;1891sun_jpeg_error_ptr jerr;1892boolean done;1893boolean mustScale = FALSE;1894boolean progressive = FALSE;1895boolean orderedBands = TRUE;1896imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);1897j_decompress_ptr cinfo;1898size_t numBytes;18991900/* verify the inputs */19011902if (data == NULL) {1903JNU_ThrowByName(env,1904"java/lang/IllegalStateException",1905"Attempting to use reader after dispose()");1906return JNI_FALSE;1907}19081909if ((buffer == NULL) || (srcBands == NULL)) {1910JNU_ThrowNullPointerException(env, 0);1911return JNI_FALSE;1912}19131914cinfo = (j_decompress_ptr) data->jpegObj;19151916if ((numBands < 1) || (numBands > MAX_BANDS) ||1917(sourceXStart < 0) || (sourceXStart >= (jint)cinfo->image_width) ||1918(sourceYStart < 0) || (sourceYStart >= (jint)cinfo->image_height) ||1919(sourceWidth < 1) || (sourceWidth > (jint)cinfo->image_width) ||1920(sourceHeight < 1) || (sourceHeight > (jint)cinfo->image_height) ||1921(stepX < 1) || (stepY < 1) ||1922(minProgressivePass < 0) ||1923(maxProgressivePass < minProgressivePass))1924{1925JNU_ThrowByName(env, "javax/imageio/IIOException",1926"Invalid argument to native readImage");1927return JNI_FALSE;1928}19291930if (stepX > (jint)cinfo->image_width) {1931stepX = cinfo->image_width;1932}1933if (stepY > (jint)cinfo->image_height) {1934stepY = cinfo->image_height;1935}19361937/*1938* First get the source bands array and copy it to our local array1939* so we don't have to worry about pinning and unpinning it again.1940*/19411942body = (*env)->GetIntArrayElements(env, srcBands, NULL);1943if (body == NULL) {1944(*env)->ExceptionClear(env);1945JNU_ThrowByName( env,1946"java/lang/OutOfMemoryError",1947"Initializing Read");1948return JNI_FALSE;1949}19501951for (i = 0; i < numBands; i++) {1952bands[i] = body[i];1953if (orderedBands && (bands[i] != i)) {1954orderedBands = FALSE;1955}1956}19571958(*env)->ReleaseIntArrayElements(env, srcBands, body, JNI_ABORT);19591960#ifdef DEBUG_IIO_JPEG1961printf("---- in reader.read ----\n");1962printf("numBands is %d\n", numBands);1963printf("bands array: ");1964for (i = 0; i < numBands; i++) {1965printf("%d ", bands[i]);1966}1967printf("\n");1968printf("jq table 0 at %p\n",1969cinfo->quant_tbl_ptrs[0]);1970#endif19711972data = (imageIODataPtr) cinfo->client_data;1973src = cinfo->src;19741975/* Set the buffer as our PixelBuffer */1976pb = &data->pixelBuf;19771978if (setPixelBuffer(env, pb, buffer) == NOT_OK) {1979return data->abortFlag; // We already threw an out of memory exception1980}19811982/* Establish the setjmp return context for sun_jpeg_error_exit to use. */1983jerr = (sun_jpeg_error_ptr) cinfo->err;19841985if (setjmp(jerr->setjmp_buffer)) {1986/* If we get here, the JPEG code has signaled an error1987while reading. */1988RELEASE_ARRAYS(env, data, src->next_input_byte);1989if (!(*env)->ExceptionOccurred(env)) {1990char buffer[JMSG_LENGTH_MAX];1991(*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,1992buffer);1993JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);1994}1995if (scanLinePtr != NULL) {1996free(scanLinePtr);1997scanLinePtr = NULL;1998}1999return data->abortFlag;2000}20012002if (GET_ARRAYS(env, data, &src->next_input_byte) == NOT_OK) {2003(*env)->ExceptionClear(env);2004JNU_ThrowByName(env,2005"javax/imageio/IIOException",2006"Array pin failed");2007return data->abortFlag;2008}20092010// If there are no tables in our structure and table arguments aren't2011// NULL, use the table arguments.2012if ((qtables != NULL) && (cinfo->quant_tbl_ptrs[0] == NULL)) {2013(void) setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);2014}20152016if ((DCHuffmanTables != NULL) && (cinfo->dc_huff_tbl_ptrs[0] == NULL)) {2017setHTables(env, (j_common_ptr) cinfo,2018DCHuffmanTables,2019ACHuffmanTables,2020TRUE);2021}20222023progressive = jpeg_has_multiple_scans(cinfo);2024if (progressive) {2025cinfo->buffered_image = TRUE;2026cinfo->input_scan_number = minProgressivePass+1; // Java count from 02027#define MAX_JAVA_INT 2147483647 // XXX Is this defined in JNI somewhere?2028if (maxProgressivePass < MAX_JAVA_INT) {2029maxProgressivePass++; // For testing2030}2031}20322033data->streamBuf.suspendable = FALSE;20342035jpeg_start_decompress(cinfo);20362037if (numBands != cinfo->output_components) {2038RELEASE_ARRAYS(env, data, src->next_input_byte);2039JNU_ThrowByName(env, "javax/imageio/IIOException",2040"Invalid argument to native readImage");2041return data->abortFlag;2042}20432044if (cinfo->output_components <= 0 ||2045cinfo->image_width > (0xffffffffu / (unsigned int)cinfo->output_components))2046{2047RELEASE_ARRAYS(env, data, src->next_input_byte);2048JNU_ThrowByName(env, "javax/imageio/IIOException",2049"Invalid number of output components");2050return data->abortFlag;2051}20522053// Allocate a 1-scanline buffer2054scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->output_components);2055if (scanLinePtr == NULL) {2056RELEASE_ARRAYS(env, data, src->next_input_byte);2057JNU_ThrowByName( env,2058"java/lang/OutOfMemoryError",2059"Reading JPEG Stream");2060return data->abortFlag;2061}20622063// loop over progressive passes2064done = FALSE;2065while (!done) {2066if (progressive) {2067// initialize the next pass. Note that this skips up to2068// the first interesting pass.2069jpeg_start_output(cinfo, cinfo->input_scan_number);2070if (wantUpdates) {2071RELEASE_ARRAYS(env, data, src->next_input_byte);2072(*env)->CallVoidMethod(env, this,2073JPEGImageReader_passStartedID,2074cinfo->input_scan_number-1);2075if ((*env)->ExceptionOccurred(env)2076|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2077cinfo->err->error_exit((j_common_ptr) cinfo);2078}2079}2080} else if (wantUpdates) {2081RELEASE_ARRAYS(env, data, src->next_input_byte);2082(*env)->CallVoidMethod(env, this,2083JPEGImageReader_passStartedID,20840);2085if ((*env)->ExceptionOccurred(env)2086|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2087cinfo->err->error_exit((j_common_ptr) cinfo);2088}2089}20902091// Skip until the first interesting line2092while ((data->abortFlag == JNI_FALSE)2093&& ((jint)cinfo->output_scanline < sourceYStart)) {2094jpeg_read_scanlines(cinfo, &scanLinePtr, 1);2095}20962097scanlineLimit = sourceYStart+sourceHeight;2098pixelLimit = scanLinePtr2099+(sourceXStart+sourceWidth)*cinfo->output_components;21002101pixelStride = stepX*cinfo->output_components;2102targetLine = 0;21032104while ((data->abortFlag == JNI_FALSE)2105&& ((jint)cinfo->output_scanline < scanlineLimit)) {21062107jpeg_read_scanlines(cinfo, &scanLinePtr, 1);21082109// Now mangle it into our buffer2110out = data->pixelBuf.buf.bp;21112112if (orderedBands && (pixelStride == numBands)) {2113// Optimization: The component bands are ordered sequentially,2114// so we can simply use memcpy() to copy the intermediate2115// scanline buffer into the raster.2116in = scanLinePtr + (sourceXStart * cinfo->output_components);2117if (pixelLimit > in) {2118numBytes = pixelLimit - in;2119if (numBytes > data->pixelBuf.byteBufferLength) {2120numBytes = data->pixelBuf.byteBufferLength;2121}2122memcpy(out, in, numBytes);2123}2124} else {2125numBytes = numBands;2126for (in = scanLinePtr+sourceXStart*cinfo->output_components;2127in < pixelLimit &&2128numBytes <= data->pixelBuf.byteBufferLength;2129in += pixelStride) {2130for (i = 0; i < numBands; i++) {2131*out++ = *(in+bands[i]);2132}2133numBytes += numBands;2134}2135}21362137// And call it back to Java2138RELEASE_ARRAYS(env, data, src->next_input_byte);2139(*env)->CallVoidMethod(env,2140this,2141JPEGImageReader_acceptPixelsID,2142targetLine++,2143progressive);21442145if ((*env)->ExceptionOccurred(env)2146|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2147cinfo->err->error_exit((j_common_ptr) cinfo);2148}21492150// And skip over uninteresting lines to the next subsampled line2151// Ensure we don't go past the end of the image21522153// Lines to skip based on subsampling2154skipLines = stepY - 1;2155// Lines left in the image2156linesLeft = scanlineLimit - cinfo->output_scanline;2157// Take the minimum2158if (skipLines > linesLeft) {2159skipLines = linesLeft;2160}2161for(i = 0; i < skipLines; i++) {2162jpeg_read_scanlines(cinfo, &scanLinePtr, 1);2163}2164}2165if (progressive) {2166jpeg_finish_output(cinfo); // Increments pass counter2167// Call Java to notify pass complete2168if (jpeg_input_complete(cinfo)2169|| (cinfo->input_scan_number > maxProgressivePass)) {2170done = TRUE;2171}2172} else {2173done = TRUE;2174}2175if (wantUpdates) {2176RELEASE_ARRAYS(env, data, src->next_input_byte);2177(*env)->CallVoidMethod(env, this,2178JPEGImageReader_passCompleteID);2179if ((*env)->ExceptionOccurred(env)2180|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {2181cinfo->err->error_exit((j_common_ptr) cinfo);2182}2183}21842185}2186/*2187* We are done, but we might not have read all the lines, or all2188* the passes, so use jpeg_abort instead of jpeg_finish_decompress.2189*/2190if ((cinfo->output_scanline != cinfo->output_height) ||2191data->abortFlag == JNI_TRUE)2192{2193jpeg_abort_decompress(cinfo);2194} else if ((!jpeg_input_complete(cinfo)) &&2195(progressive &&2196(cinfo->input_scan_number > maxProgressivePass))) {2197/* We haven't reached EOI, but we need to skip to there */2198(*cinfo->src->term_source) (cinfo);2199/* We can use jpeg_abort to release memory and reset global_state */2200jpeg_abort((j_common_ptr) cinfo);2201(*env)->CallVoidMethod(env,2202this,2203JPEGImageReader_skipPastImageID,2204imageIndex);2205} else {2206jpeg_finish_decompress(cinfo);2207}22082209free(scanLinePtr);22102211RELEASE_ARRAYS(env, data, src->next_input_byte);22122213return data->abortFlag;2214}22152216JNIEXPORT void JNICALL2217Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_abortRead2218(JNIEnv *env,2219jobject this,2220jlong ptr) {22212222imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);22232224if (data == NULL) {2225JNU_ThrowByName(env,2226"java/lang/IllegalStateException",2227"Attempting to use reader after dispose()");2228return;2229}22302231imageio_abort(env, this, data);22322233}22342235JNIEXPORT void JNICALL2236Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetLibraryState2237(JNIEnv *env,2238jobject this,2239jlong ptr) {2240imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2241j_decompress_ptr cinfo;22422243if (data == NULL) {2244JNU_ThrowByName(env,2245"java/lang/IllegalStateException",2246"Attempting to use reader after dispose()");2247return;2248}22492250cinfo = (j_decompress_ptr) data->jpegObj;22512252jpeg_abort_decompress(cinfo);2253}225422552256JNIEXPORT void JNICALL2257Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetReader2258(JNIEnv *env,2259jobject this,2260jlong ptr) {22612262imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2263j_decompress_ptr cinfo;2264sun_jpeg_error_ptr jerr;22652266if (data == NULL) {2267JNU_ThrowByName(env,2268"java/lang/IllegalStateException",2269"Attempting to use reader after dispose()");2270return;2271}22722273cinfo = (j_decompress_ptr) data->jpegObj;22742275jerr = (sun_jpeg_error_ptr) cinfo->err;22762277imageio_reset(env, (j_common_ptr) cinfo, data);22782279/*2280* The tables have not been reset, and there is no way to do so2281* in IJG without leaking memory. The only situation in which2282* this will cause a problem is if an image-only stream is read2283* with this object without initializing the correct tables first.2284* This situation, which should cause an error, might work but2285* produce garbage instead. If the huffman tables are wrong,2286* it will fail during the decode. If the q tables are wrong, it2287* will look strange. This is very unlikely, so don't worry about2288* it. To be really robust, we would keep a flag for table state2289* and consult it to catch exceptional situations.2290*/22912292/* above does not clean up the source, so we have to */22932294/*2295We need to explicitly initialize exception handler or we may2296longjump to random address from the term_source()2297*/22982299if (setjmp(jerr->setjmp_buffer)) {23002301/*2302We may get IOException from pushBack() here.23032304However it could be legal if original input stream was closed2305earlier (for example because network connection was closed).2306Unfortunately, image inputstream API has no way to check whether2307stream is already closed or IOException was thrown because of some2308other IO problem,2309And we can not avoid call to pushBack() on closed stream for the2310same reason.23112312So, for now we will silently eat this exception.23132314NB: this may be changed in future when ImageInputStream API will2315become more flexible.2316*/23172318if ((*env)->ExceptionOccurred(env)) {2319(*env)->ExceptionClear(env);2320}2321} else {2322cinfo->src->term_source(cinfo);2323}23242325cinfo->src->bytes_in_buffer = 0;2326cinfo->src->next_input_byte = NULL;2327}23282329JNIEXPORT void JNICALL2330Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_disposeReader2331(JNIEnv *env,2332jclass reader,2333jlong ptr) {23342335imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2336j_common_ptr info = destroyImageioData(env, data);23372338imageio_dispose(info);2339}23402341/********************** end of Reader *************************/23422343/********************** Writer Support ************************/23442345/********************** Destination Manager *******************/23462347METHODDEF(void)2348/*2349* Initialize destination --- called by jpeg_start_compress2350* before any data is actually written. The data arrays2351* must be pinned before this is called.2352*/2353imageio_init_destination (j_compress_ptr cinfo)2354{2355struct jpeg_destination_mgr *dest = cinfo->dest;2356imageIODataPtr data = (imageIODataPtr) cinfo->client_data;2357streamBufferPtr sb = &data->streamBuf;2358JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);23592360if (sb->buf == NULL) {2361// We forgot to pin the array2362(*env)->FatalError(env, "Output buffer not pinned!");2363}23642365dest->next_output_byte = sb->buf;2366dest->free_in_buffer = sb->bufferLength;2367}23682369/*2370* Empty the output buffer --- called whenever buffer fills up.2371*2372* This routine writes the entire output buffer2373* (ignoring the current state of next_output_byte & free_in_buffer),2374* resets the pointer & count to the start of the buffer, and returns TRUE2375* indicating that the buffer has been dumped.2376*/23772378METHODDEF(boolean)2379imageio_empty_output_buffer (j_compress_ptr cinfo)2380{2381struct jpeg_destination_mgr *dest = cinfo->dest;2382imageIODataPtr data = (imageIODataPtr) cinfo->client_data;2383streamBufferPtr sb = &data->streamBuf;2384JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);2385jobject output = NULL;23862387RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));23882389GET_IO_REF(output);23902391(*env)->CallVoidMethod(env,2392output,2393JPEGImageWriter_writeOutputDataID,2394sb->hstreamBuffer,23950,2396sb->bufferLength);2397if ((*env)->ExceptionOccurred(env)2398|| !GET_ARRAYS(env, data,2399(const JOCTET **)(&dest->next_output_byte))) {2400cinfo->err->error_exit((j_common_ptr) cinfo);2401}24022403dest->next_output_byte = sb->buf;2404dest->free_in_buffer = sb->bufferLength;24052406return TRUE;2407}24082409/*2410* After all of the data has been encoded there may still be some2411* more left over in some of the working buffers. Now is the2412* time to clear them out.2413*/2414METHODDEF(void)2415imageio_term_destination (j_compress_ptr cinfo)2416{2417struct jpeg_destination_mgr *dest = cinfo->dest;2418imageIODataPtr data = (imageIODataPtr) cinfo->client_data;2419streamBufferPtr sb = &data->streamBuf;2420JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);24212422/* find out how much needs to be written */2423/* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */2424jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer);24252426if (datacount != 0) {2427jobject output = NULL;24282429RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));24302431GET_IO_REF(output);24322433(*env)->CallVoidMethod(env,2434output,2435JPEGImageWriter_writeOutputDataID,2436sb->hstreamBuffer,24370,2438datacount);24392440if ((*env)->ExceptionOccurred(env)2441|| !GET_ARRAYS(env, data,2442(const JOCTET **)(&dest->next_output_byte))) {2443cinfo->err->error_exit((j_common_ptr) cinfo);2444}2445}24462447dest->next_output_byte = NULL;2448dest->free_in_buffer = 0;24492450}24512452/*2453* Flush the destination buffer. This is not called by the library,2454* but by our code below. This is the simplest implementation, though2455* certainly not the most efficient.2456*/2457METHODDEF(void)2458imageio_flush_destination(j_compress_ptr cinfo)2459{2460imageio_term_destination(cinfo);2461imageio_init_destination(cinfo);2462}24632464/********************** end of destination manager ************/24652466/********************** Writer JNI calls **********************/246724682469JNIEXPORT void JNICALL2470Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initWriterIDs2471(JNIEnv *env,2472jclass cls,2473jclass qTableClass,2474jclass huffClass) {24752476CHECK_NULL(JPEGImageWriter_writeOutputDataID = (*env)->GetMethodID(env,2477cls,2478"writeOutputData",2479"([BII)V"));2480CHECK_NULL(JPEGImageWriter_warningOccurredID = (*env)->GetMethodID(env,2481cls,2482"warningOccurred",2483"(I)V"));2484CHECK_NULL(JPEGImageWriter_warningWithMessageID =2485(*env)->GetMethodID(env,2486cls,2487"warningWithMessage",2488"(Ljava/lang/String;)V"));2489CHECK_NULL(JPEGImageWriter_writeMetadataID = (*env)->GetMethodID(env,2490cls,2491"writeMetadata",2492"()V"));2493CHECK_NULL(JPEGImageWriter_grabPixelsID = (*env)->GetMethodID(env,2494cls,2495"grabPixels",2496"(I)V"));2497CHECK_NULL(JPEGQTable_tableID = (*env)->GetFieldID(env,2498qTableClass,2499"qTable",2500"[I"));2501CHECK_NULL(JPEGHuffmanTable_lengthsID = (*env)->GetFieldID(env,2502huffClass,2503"lengths",2504"[S"));2505CHECK_NULL(JPEGHuffmanTable_valuesID = (*env)->GetFieldID(env,2506huffClass,2507"values",2508"[S"));2509}25102511JNIEXPORT jlong JNICALL2512Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initJPEGImageWriter2513(JNIEnv *env,2514jobject this) {25152516imageIODataPtr ret;2517struct sun_jpeg_error_mgr *jerr;2518struct jpeg_destination_mgr *dest;25192520/* This struct contains the JPEG compression parameters and pointers to2521* working space (which is allocated as needed by the JPEG library).2522*/2523struct jpeg_compress_struct *cinfo =2524malloc(sizeof(struct jpeg_compress_struct));2525if (cinfo == NULL) {2526JNU_ThrowByName( env,2527"java/lang/OutOfMemoryError",2528"Initializing Writer");2529return 0;2530}25312532/* We use our private extension JPEG error handler.2533*/2534jerr = malloc (sizeof(struct sun_jpeg_error_mgr));2535if (jerr == NULL) {2536JNU_ThrowByName( env,2537"java/lang/OutOfMemoryError",2538"Initializing Writer");2539free(cinfo);2540return 0;2541}25422543/* We set up the normal JPEG error routines, then override error_exit. */2544cinfo->err = jpeg_std_error(&(jerr->pub));2545jerr->pub.error_exit = sun_jpeg_error_exit;2546/* We need to setup our own print routines */2547jerr->pub.output_message = sun_jpeg_output_message;2548/* Now we can setjmp before every call to the library */25492550/* Establish the setjmp return context for sun_jpeg_error_exit to use. */2551if (setjmp(jerr->setjmp_buffer)) {2552/* If we get here, the JPEG code has signaled an error. */2553char buffer[JMSG_LENGTH_MAX];2554(*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,2555buffer);2556JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);2557return 0;2558}25592560/* Perform library initialization */2561jpeg_create_compress(cinfo);25622563/* Now set up the destination */2564dest = malloc(sizeof(struct jpeg_destination_mgr));2565if (dest == NULL) {2566JNU_ThrowByName( env,2567"java/lang/OutOfMemoryError",2568"Initializing Writer");2569imageio_dispose((j_common_ptr)cinfo);2570return 0;2571}25722573dest->init_destination = imageio_init_destination;2574dest->empty_output_buffer = imageio_empty_output_buffer;2575dest->term_destination = imageio_term_destination;2576dest->next_output_byte = NULL;2577dest->free_in_buffer = 0;25782579cinfo->dest = dest;25802581/* set up the association to persist for future calls */2582ret = initImageioData(env, (j_common_ptr) cinfo, this);2583if (ret == NULL) {2584(*env)->ExceptionClear(env);2585JNU_ThrowByName( env,2586"java/lang/OutOfMemoryError",2587"Initializing Writer");2588imageio_dispose((j_common_ptr)cinfo);2589return 0;2590}2591return ptr_to_jlong(ret);2592}25932594JNIEXPORT void JNICALL2595Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_setDest2596(JNIEnv *env,2597jobject this,2598jlong ptr) {25992600imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2601j_compress_ptr cinfo;26022603if (data == NULL) {2604JNU_ThrowByName(env,2605"java/lang/IllegalStateException",2606"Attempting to use writer after dispose()");2607return;2608}26092610cinfo = (j_compress_ptr) data->jpegObj;26112612imageio_set_stream(env, data->jpegObj, data, this);261326142615// Don't call the init method, as that depends on pinned arrays2616cinfo->dest->next_output_byte = NULL;2617cinfo->dest->free_in_buffer = 0;2618}26192620JNIEXPORT void JNICALL2621Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables2622(JNIEnv *env,2623jobject this,2624jlong ptr,2625jobjectArray qtables,2626jobjectArray DCHuffmanTables,2627jobjectArray ACHuffmanTables) {26282629struct jpeg_destination_mgr *dest;2630sun_jpeg_error_ptr jerr;2631imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2632j_compress_ptr cinfo;26332634if (data == NULL) {2635JNU_ThrowByName(env,2636"java/lang/IllegalStateException",2637"Attempting to use writer after dispose()");2638return;2639}26402641cinfo = (j_compress_ptr) data->jpegObj;2642dest = cinfo->dest;26432644/* Establish the setjmp return context for sun_jpeg_error_exit to use. */2645jerr = (sun_jpeg_error_ptr) cinfo->err;26462647if (setjmp(jerr->setjmp_buffer)) {2648/* If we get here, the JPEG code has signaled an error2649while writing. */2650RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));2651if (!(*env)->ExceptionOccurred(env)) {2652char buffer[JMSG_LENGTH_MAX];2653(*cinfo->err->format_message) ((j_common_ptr) cinfo,2654buffer);2655JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);2656}2657return;2658}26592660if (GET_ARRAYS(env, data,2661(const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {2662(*env)->ExceptionClear(env);2663JNU_ThrowByName(env,2664"javax/imageio/IIOException",2665"Array pin failed");2666return;2667}26682669jpeg_suppress_tables(cinfo, TRUE); // Suppress writing of any current26702671data->streamBuf.suspendable = FALSE;2672if (qtables != NULL) {2673#ifdef DEBUG_IIO_JPEG2674printf("in writeTables: qtables not NULL\n");2675#endif2676setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);2677}26782679if (DCHuffmanTables != NULL) {2680setHTables(env, (j_common_ptr) cinfo,2681DCHuffmanTables, ACHuffmanTables, TRUE);2682}26832684jpeg_write_tables(cinfo); // Flushes the buffer for you2685RELEASE_ARRAYS(env, data, NULL);2686}26872688static void freeArray(void** arr, jint size) {2689int i;2690if (arr != NULL) {2691for (i = 0; i < size; i++) {2692if (arr[i] != NULL) {2693free(arr[i]);2694}2695}2696free(arr);2697}2698}26992700JNIEXPORT jboolean JNICALL2701Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage2702(JNIEnv *env,2703jobject this,2704jlong ptr,2705jbyteArray buffer,2706jint inCs, jint outCs,2707jint numBands,2708jintArray bandSizes,2709jint srcWidth,2710jint destWidth, jint destHeight,2711jint stepX, jint stepY,2712jobjectArray qtables,2713jboolean writeDQT,2714jobjectArray DCHuffmanTables,2715jobjectArray ACHuffmanTables,2716jboolean writeDHT,2717jboolean optimize,2718jboolean progressive,2719jint numScans,2720jintArray scanInfo,2721jintArray componentIds,2722jintArray HsamplingFactors,2723jintArray VsamplingFactors,2724jintArray QtableSelectors,2725jboolean haveMetadata,2726jint restartInterval) {27272728struct jpeg_destination_mgr *dest;2729JSAMPROW scanLinePtr;2730int i, j;2731int pixelStride;2732unsigned char *in, *out, *pixelLimit, *scanLineLimit;2733unsigned int scanLineSize, pixelBufferSize;2734int targetLine;2735pixelBufferPtr pb;2736sun_jpeg_error_ptr jerr;2737jint *ids, *hfactors, *vfactors, *qsels;2738jsize qlen, hlen;2739int *scanptr;2740jint *scanData;2741jint *bandSize;2742int maxBandValue, halfMaxBandValue;2743imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);2744j_compress_ptr cinfo;2745UINT8** scale = NULL;2746boolean success = TRUE;274727482749/* verify the inputs */27502751if (data == NULL) {2752JNU_ThrowByName(env,2753"java/lang/IllegalStateException",2754"Attempting to use writer after dispose()");2755return JNI_FALSE;2756}27572758if ((buffer == NULL) ||2759(qtables == NULL) ||2760// H tables can be null if optimizing2761(componentIds == NULL) ||2762(HsamplingFactors == NULL) || (VsamplingFactors == NULL) ||2763(QtableSelectors == NULL) ||2764((numScans != 0) && (scanInfo != NULL))) {27652766JNU_ThrowNullPointerException(env, 0);2767return JNI_FALSE;27682769}27702771scanLineSize = destWidth * numBands;2772if ((inCs < 0) || (inCs > JCS_YCCK) ||2773(outCs < 0) || (outCs > JCS_YCCK) ||2774(numBands < 1) || (numBands > MAX_BANDS) ||2775(srcWidth < 0) ||2776(destWidth < 0) || (destWidth > srcWidth) ||2777(destHeight < 0) ||2778(stepX < 0) || (stepY < 0) ||2779((INT_MAX / numBands) < destWidth)) /* destWidth causes an integer overflow */2780{2781JNU_ThrowByName(env, "javax/imageio/IIOException",2782"Invalid argument to native writeImage");2783return JNI_FALSE;2784}27852786if (stepX > srcWidth) {2787stepX = srcWidth;2788}27892790bandSize = (*env)->GetIntArrayElements(env, bandSizes, NULL);2791CHECK_NULL_RETURN(bandSize, JNI_FALSE);27922793for (i = 0; i < numBands; i++) {2794if (bandSize[i] <= 0 || bandSize[i] > JPEG_BAND_SIZE) {2795(*env)->ReleaseIntArrayElements(env, bandSizes,2796bandSize, JNI_ABORT);2797JNU_ThrowByName(env, "javax/imageio/IIOException", "Invalid Image");2798return JNI_FALSE;2799}2800}28012802for (i = 0; i < numBands; i++) {2803if (bandSize[i] != JPEG_BAND_SIZE) {2804if (scale == NULL) {2805scale = (UINT8**) calloc(numBands, sizeof(UINT8*));28062807if (scale == NULL) {2808(*env)->ReleaseIntArrayElements(env, bandSizes,2809bandSize, JNI_ABORT);2810JNU_ThrowByName( env, "java/lang/OutOfMemoryError",2811"Writing JPEG Stream");2812return JNI_FALSE;2813}2814}28152816maxBandValue = (1 << bandSize[i]) - 1;28172818scale[i] = (UINT8*) malloc((maxBandValue + 1) * sizeof(UINT8));28192820if (scale[i] == NULL) {2821// Cleanup before throwing an out of memory exception2822for (j = 0; j < i; j++) {2823free(scale[j]);2824}2825free(scale);2826(*env)->ReleaseIntArrayElements(env, bandSizes,2827bandSize, JNI_ABORT);2828JNU_ThrowByName( env, "java/lang/OutOfMemoryError",2829"Writing JPEG Stream");2830return JNI_FALSE;2831}28322833halfMaxBandValue = maxBandValue >> 1;28342835for (j = 0; j <= maxBandValue; j++) {2836scale[i][j] = (UINT8)2837((j*MAX_JPEG_BAND_VALUE + halfMaxBandValue)/maxBandValue);2838}2839}2840}28412842(*env)->ReleaseIntArrayElements(env, bandSizes,2843bandSize, JNI_ABORT);28442845cinfo = (j_compress_ptr) data->jpegObj;2846dest = cinfo->dest;28472848/* Set the buffer as our PixelBuffer */2849pb = &data->pixelBuf;28502851if (setPixelBuffer(env, pb, buffer) == NOT_OK) {2852freeArray(scale, numBands);2853return data->abortFlag; // We already threw an out of memory exception2854}28552856// Allocate a 1-scanline buffer2857scanLinePtr = (JSAMPROW)malloc(scanLineSize);2858if (scanLinePtr == NULL) {2859freeArray(scale, numBands);2860JNU_ThrowByName( env,2861"java/lang/OutOfMemoryError",2862"Writing JPEG Stream");2863return data->abortFlag;2864}2865scanLineLimit = scanLinePtr + scanLineSize;28662867/* Establish the setjmp return context for sun_jpeg_error_exit to use. */2868jerr = (sun_jpeg_error_ptr) cinfo->err;28692870if (setjmp(jerr->setjmp_buffer)) {2871/* If we get here, the JPEG code has signaled an error2872while writing. */2873RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));2874if (!(*env)->ExceptionOccurred(env)) {2875char buffer[JMSG_LENGTH_MAX];2876(*cinfo->err->format_message) ((j_common_ptr) cinfo,2877buffer);2878JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);2879}28802881freeArray(scale, numBands);2882free(scanLinePtr);2883return data->abortFlag;2884}28852886// set up parameters2887cinfo->image_width = destWidth;2888cinfo->image_height = destHeight;2889cinfo->input_components = numBands;2890cinfo->in_color_space = inCs;28912892jpeg_set_defaults(cinfo);28932894jpeg_set_colorspace(cinfo, outCs);28952896cinfo->optimize_coding = optimize;28972898cinfo->write_JFIF_header = FALSE;2899cinfo->write_Adobe_marker = FALSE;2900// copy componentIds2901ids = (*env)->GetIntArrayElements(env, componentIds, NULL);2902hfactors = (*env)->GetIntArrayElements(env, HsamplingFactors, NULL);2903vfactors = (*env)->GetIntArrayElements(env, VsamplingFactors, NULL);2904qsels = (*env)->GetIntArrayElements(env, QtableSelectors, NULL);29052906if (ids && hfactors && vfactors && qsels) {2907for (i = 0; i < numBands; i++) {2908cinfo->comp_info[i].component_id = ids[i];2909cinfo->comp_info[i].h_samp_factor = hfactors[i];2910cinfo->comp_info[i].v_samp_factor = vfactors[i];2911cinfo->comp_info[i].quant_tbl_no = qsels[i];2912}2913} else {2914success = FALSE;2915}29162917if (ids) {2918(*env)->ReleaseIntArrayElements(env, componentIds, ids, JNI_ABORT);2919}2920if (hfactors) {2921(*env)->ReleaseIntArrayElements(env, HsamplingFactors, hfactors, JNI_ABORT);2922}2923if (vfactors) {2924(*env)->ReleaseIntArrayElements(env, VsamplingFactors, vfactors, JNI_ABORT);2925}2926if (qsels) {2927(*env)->ReleaseIntArrayElements(env, QtableSelectors, qsels, JNI_ABORT);2928}2929if (!success) {2930freeArray(scale, numBands);2931free(scanLinePtr);2932return data->abortFlag;2933}29342935jpeg_suppress_tables(cinfo, TRUE); // Disable writing any current29362937qlen = setQTables(env, (j_common_ptr) cinfo, qtables, writeDQT);29382939if (!optimize) {2940// Set the h tables2941hlen = setHTables(env,2942(j_common_ptr) cinfo,2943DCHuffmanTables,2944ACHuffmanTables,2945writeDHT);2946}29472948if (GET_ARRAYS(env, data,2949(const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {2950(*env)->ExceptionClear(env);2951freeArray(scale, numBands);2952free(scanLinePtr);2953JNU_ThrowByName(env,2954"javax/imageio/IIOException",2955"Array pin failed");2956return data->abortFlag;2957}29582959data->streamBuf.suspendable = FALSE;29602961if (progressive) {2962if (numScans == 0) { // then use default scans2963jpeg_simple_progression(cinfo);2964} else {2965cinfo->num_scans = numScans;2966// Copy the scanInfo to a local array2967// The following is copied from jpeg_simple_progression:2968/* Allocate space for script.2969* We need to put it in the permanent pool in case the application performs2970* multiple compressions without changing the settings. To avoid a memory2971* leak if jpeg_simple_progression is called repeatedly for the same JPEG2972* object, we try to re-use previously allocated space, and we allocate2973* enough space to handle YCbCr even if initially asked for grayscale.2974*/2975if (cinfo->script_space == NULL2976|| cinfo->script_space_size < numScans) {2977cinfo->script_space_size = MAX(numScans, 10);2978cinfo->script_space = (jpeg_scan_info *)2979(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,2980JPOOL_PERMANENT,2981cinfo->script_space_size2982* sizeof(jpeg_scan_info));2983}2984cinfo->scan_info = cinfo->script_space;2985scanptr = (int *) cinfo->script_space;2986scanData = (*env)->GetIntArrayElements(env, scanInfo, NULL);2987if (scanData == NULL) {2988RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));2989freeArray(scale, numBands);2990free(scanLinePtr);2991return data->abortFlag;2992}2993// number of jints per scan is 92994// We avoid a memcpy to handle different size ints2995for (i = 0; i < numScans*9; i++) {2996scanptr[i] = scanData[i];2997}2998(*env)->ReleaseIntArrayElements(env, scanInfo,2999scanData, JNI_ABORT);30003001}3002}30033004cinfo->restart_interval = restartInterval;30053006#ifdef DEBUG_IIO_JPEG3007printf("writer setup complete, starting compressor\n");3008#endif30093010// start the compressor; tables must already be set3011jpeg_start_compress(cinfo, FALSE); // Leaves sent_table alone30123013if (haveMetadata) {3014// Flush the buffer3015imageio_flush_destination(cinfo);3016// Call Java to write the metadata3017RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));3018(*env)->CallVoidMethod(env,3019this,3020JPEGImageWriter_writeMetadataID);3021if ((*env)->ExceptionOccurred(env)3022|| !GET_ARRAYS(env, data,3023(const JOCTET **)(&dest->next_output_byte))) {3024cinfo->err->error_exit((j_common_ptr) cinfo);3025}3026}30273028targetLine = 0;3029pixelBufferSize = srcWidth * numBands;3030pixelStride = numBands * stepX;30313032// for each line in destHeight3033while ((data->abortFlag == JNI_FALSE)3034&& (cinfo->next_scanline < cinfo->image_height)) {3035// get the line from Java3036RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));3037(*env)->CallVoidMethod(env,3038this,3039JPEGImageWriter_grabPixelsID,3040targetLine);3041if ((*env)->ExceptionOccurred(env)3042|| !GET_ARRAYS(env, data,3043(const JOCTET **)(&dest->next_output_byte))) {3044cinfo->err->error_exit((j_common_ptr) cinfo);3045}30463047// subsample it into our buffer30483049in = data->pixelBuf.buf.bp;3050out = scanLinePtr;3051pixelLimit = in + ((pixelBufferSize > data->pixelBuf.byteBufferLength) ?3052data->pixelBuf.byteBufferLength : pixelBufferSize);3053for (; (in < pixelLimit) && (out < scanLineLimit); in += pixelStride) {3054for (i = 0; i < numBands; i++) {3055if (scale !=NULL && scale[i] != NULL) {3056*out++ = scale[i][*(in+i)];3057#ifdef DEBUG_IIO_JPEG3058if (in == data->pixelBuf.buf.bp){ // Just the first pixel3059printf("in %d -> out %d, ", *(in+i), *(out-i-1));3060}3061#endif30623063#ifdef DEBUG_IIO_JPEG3064if (in == data->pixelBuf.buf.bp){ // Just the first pixel3065printf("\n");3066}3067#endif3068} else {3069*out++ = *(in+i);3070}3071}3072}3073// write it out3074jpeg_write_scanlines(cinfo, (JSAMPARRAY)&scanLinePtr, 1);3075targetLine += stepY;3076}30773078/*3079* We are done, but we might not have done all the lines,3080* so use jpeg_abort instead of jpeg_finish_compress.3081*/3082if (cinfo->next_scanline == cinfo->image_height) {3083jpeg_finish_compress(cinfo); // Flushes buffer with term_dest3084} else {3085jpeg_abort((j_common_ptr)cinfo);3086}30873088freeArray(scale, numBands);3089free(scanLinePtr);3090RELEASE_ARRAYS(env, data, NULL);3091return data->abortFlag;3092}30933094JNIEXPORT void JNICALL3095Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_abortWrite3096(JNIEnv *env,3097jobject this,3098jlong ptr) {30993100imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);31013102if (data == NULL) {3103JNU_ThrowByName(env,3104"java/lang/IllegalStateException",3105"Attempting to use writer after dispose()");3106return;3107}31083109imageio_abort(env, this, data);3110}31113112JNIEXPORT void JNICALL3113Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_resetWriter3114(JNIEnv *env,3115jobject this,3116jlong ptr) {3117imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);3118j_compress_ptr cinfo;31193120if (data == NULL) {3121JNU_ThrowByName(env,3122"java/lang/IllegalStateException",3123"Attempting to use writer after dispose()");3124return;3125}31263127cinfo = (j_compress_ptr) data->jpegObj;31283129imageio_reset(env, (j_common_ptr) cinfo, data);31303131/*3132* The tables have not been reset, and there is no way to do so3133* in IJG without leaking memory. The only situation in which3134* this will cause a problem is if an image-only stream is written3135* with this object without initializing the correct tables first,3136* which should not be possible.3137*/31383139cinfo->dest->next_output_byte = NULL;3140cinfo->dest->free_in_buffer = 0;3141}31423143JNIEXPORT void JNICALL3144Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_disposeWriter3145(JNIEnv *env,3146jclass writer,3147jlong ptr) {31483149imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);3150j_common_ptr info = destroyImageioData(env, data);31513152imageio_dispose(info);3153}315431553156