1 /*
   2  * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /*
  27  * This file contains the code to link the Java Image I/O JPEG plug-in
  28  * to the IJG library used to read and write JPEG files.  Much of it has
  29  * been copied, updated, and annotated from the jpegdecoder.c AWT JPEG
  30  * decoder.  Where that code was unclear, the present author has either
  31  * rewritten the relevant section or commented it for the sake of future
  32  * maintainers.
  33  *
  34  * In particular, the way the AWT code handled progressive JPEGs seems
  35  * to me to be only accidentally correct and somewhat inefficient.  The
  36  * scheme used here represents the way I think it should work. (REV 11/00)
  37  */
  38 
  39 #include <stdlib.h>
  40 #include <setjmp.h>
  41 #include <assert.h>
  42 #include <string.h>
  43 #include <limits.h>
  44 
  45 /* java native interface headers */
  46 #include "jni.h"
  47 #include "jni_util.h"
  48 
  49 #include "com_sun_imageio_plugins_jpeg_JPEGImageReader.h"
  50 #include "com_sun_imageio_plugins_jpeg_JPEGImageWriter.h"
  51 
  52 /* headers from the JPEG library */
  53 #include <jpeglib.h>
  54 #include "jerror.h"
  55 
  56 #undef MAX
  57 #define MAX(a,b)        ((a) > (b) ? (a) : (b))
  58 
  59 #ifdef __APPLE__
  60 /* use setjmp/longjmp versions that do not save/restore the signal mask */
  61 #define setjmp _setjmp
  62 #define longjmp _longjmp
  63 #endif
  64 
  65 /* Cached Java method ids */
  66 static jmethodID JPEGImageReader_readInputDataID;
  67 static jmethodID JPEGImageReader_skipInputBytesID;
  68 static jmethodID JPEGImageReader_warningOccurredID;
  69 static jmethodID JPEGImageReader_warningWithMessageID;
  70 static jmethodID JPEGImageReader_setImageDataID;
  71 static jmethodID JPEGImageReader_acceptPixelsID;
  72 static jmethodID JPEGImageReader_pushBackID;
  73 static jmethodID JPEGImageReader_passStartedID;
  74 static jmethodID JPEGImageReader_passCompleteID;
  75 static jmethodID JPEGImageWriter_writeOutputDataID;
  76 static jmethodID JPEGImageWriter_warningOccurredID;
  77 static jmethodID JPEGImageWriter_warningWithMessageID;
  78 static jmethodID JPEGImageWriter_writeMetadataID;
  79 static jmethodID JPEGImageWriter_grabPixelsID;
  80 static jfieldID JPEGQTable_tableID;
  81 static jfieldID JPEGHuffmanTable_lengthsID;
  82 static jfieldID JPEGHuffmanTable_valuesID;
  83 
  84 /*
  85  * Defined in jpegdecoder.c.  Copy code from there if and
  86  * when that disappears. */
  87 extern JavaVM *jvm;
  88 
  89 /*
  90  * The following sets of defines must match the warning messages in the
  91  * Java code.
  92  */
  93 
  94 /* Reader warnings */
  95 #define READ_NO_EOI          0
  96 
  97 /* Writer warnings */
  98 
  99 /* Return codes for various ops */
 100 #define OK     1
 101 #define NOT_OK 0
 102 
 103 /*
 104  * First we define two objects, one for the stream and buffer and one
 105  * for pixels.  Both contain references to Java objects and pointers to
 106  * pinned arrays.  These objects can be used for either input or
 107  * output.  Pixels can be accessed as either INT32s or bytes.
 108  * Every I/O operation will have one of each these objects, one for
 109  * the stream and the other to hold pixels, regardless of the I/O direction.
 110  */
 111 
 112 /******************** StreamBuffer definition ************************/
 113 
 114 typedef struct streamBufferStruct {
 115     jweak ioRef;               // weak reference to a provider of I/O routines
 116     jbyteArray hstreamBuffer;  // Handle to a Java buffer for the stream
 117     JOCTET *buf;               // Pinned buffer pointer */
 118     size_t bufferOffset;          // holds offset between unpin and the next pin
 119     size_t bufferLength;          // Allocated, nut just used
 120     int suspendable;           // Set to true to suspend input
 121     long remaining_skip;       // Used only on input
 122 } streamBuffer, *streamBufferPtr;
 123 
 124 /*
 125  * This buffer size was set to 64K in the old classes, 4K by default in the
 126  * IJG library, with the comment "an efficiently freadable size", and 1K
 127  * in AWT.
 128  * Unlike in the other Java designs, these objects will persist, so 64K
 129  * seems too big and 1K seems too small.  If 4K was good enough for the
 130  * IJG folks, it's good enough for me.
 131  */
 132 #define STREAMBUF_SIZE 4096
 133 
 134 #define GET_IO_REF(io_name)                                            \
 135     do {                                                               \
 136         if ((*env)->IsSameObject(env, sb->ioRef, NULL) ||              \
 137             ((io_name) = (*env)->NewLocalRef(env, sb->ioRef)) == NULL) \
 138         {                                                              \
 139             cinfo->err->error_exit((j_common_ptr) cinfo);              \
 140         }                                                              \
 141     } while (0)                                                        \
 142 
 143 /*
 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)
 148 
 149 // Forward reference
 150 static void resetStreamBuffer(JNIEnv *env, streamBufferPtr sb);
 151 
 152 /*
 153  * Initialize a freshly allocated StreamBuffer object.  The stream is left
 154  * null, as it will be set from Java by setSource, but the buffer object
 155  * is created and a global reference kept.  Returns OK on success, NOT_OK
 156  * if allocating the buffer or getting a global reference for it failed.
 157  */
 158 static int initStreamBuffer(JNIEnv *env, streamBufferPtr sb) {
 159     /* Initialize a new buffer */
 160     jbyteArray hInputBuffer = (*env)->NewByteArray(env, STREAMBUF_SIZE);
 161     if (hInputBuffer == NULL) {
 162         (*env)->ExceptionClear(env);
 163         JNU_ThrowByName( env,
 164                          "java/lang/OutOfMemoryError",
 165                          "Initializing Reader");
 166         return NOT_OK;
 167     }
 168     sb->bufferLength = (*env)->GetArrayLength(env, hInputBuffer);
 169     sb->hstreamBuffer = (*env)->NewGlobalRef(env, hInputBuffer);
 170     if (sb->hstreamBuffer == NULL) {
 171         JNU_ThrowByName( env,
 172                          "java/lang/OutOfMemoryError",
 173                          "Initializing Reader");
 174         return NOT_OK;
 175     }
 176 
 177 
 178     sb->ioRef = NULL;
 179 
 180     sb->buf = NULL;
 181 
 182     resetStreamBuffer(env, sb);
 183 
 184     return OK;
 185 }
 186 
 187 /*
 188  * Free all resources associated with this streamBuffer.  This must
 189  * be called to dispose the object to avoid leaking global references, as
 190  * resetStreamBuffer does not release the buffer reference.
 191  */
 192 static void destroyStreamBuffer(JNIEnv *env, streamBufferPtr sb) {
 193     resetStreamBuffer(env, sb);
 194     if (sb->hstreamBuffer != NULL) {
 195         (*env)->DeleteGlobalRef(env, sb->hstreamBuffer);
 196     }
 197 }
 198 
 199 // Forward reference
 200 static void unpinStreamBuffer(JNIEnv *env,
 201                               streamBufferPtr sb,
 202                               const 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 reference
 206  * to the buffer is retained.  The buffer is unpinned if it was pinned.
 207  * All other state is reset.
 208  */
 209 static void resetStreamBuffer(JNIEnv *env, streamBufferPtr sb) {
 210     if (sb->ioRef != NULL) {
 211         (*env)->DeleteWeakGlobalRef(env, sb->ioRef);
 212         sb->ioRef = NULL;
 213     }
 214     unpinStreamBuffer(env, sb, NULL);
 215     sb->bufferOffset = NO_DATA;
 216     sb->suspendable = FALSE;
 217     sb->remaining_skip = 0;
 218 }
 219 
 220 /*
 221  * Pins the data buffer associated with this stream.  Returns OK on
 222  * success, NOT_OK on failure, as GetPrimitiveArrayCritical may fail.
 223  */
 224 static int pinStreamBuffer(JNIEnv *env,
 225                            streamBufferPtr sb,
 226                            const JOCTET **next_byte) {
 227     if (sb->hstreamBuffer != NULL) {
 228         assert(sb->buf == NULL);
 229         sb->buf =
 230             (JOCTET *)(*env)->GetPrimitiveArrayCritical(env,
 231                                                         sb->hstreamBuffer,
 232                                                         NULL);
 233         if (sb->buf == NULL) {
 234             return NOT_OK;
 235         }
 236         if (sb->bufferOffset != NO_DATA) {
 237             *next_byte = sb->buf + sb->bufferOffset;
 238         }
 239     }
 240     return OK;
 241 }
 242 
 243 /*
 244  * Unpins the data buffer associated with this stream.
 245  */
 246 static void unpinStreamBuffer(JNIEnv *env,
 247                               streamBufferPtr sb,
 248                               const JOCTET *next_byte) {
 249     if (sb->buf != NULL) {
 250         assert(sb->hstreamBuffer != NULL);
 251         if (next_byte == NULL) {
 252             sb->bufferOffset = NO_DATA;
 253         } else {
 254             sb->bufferOffset = next_byte - sb->buf;
 255         }
 256         (*env)->ReleasePrimitiveArrayCritical(env,
 257                                               sb->hstreamBuffer,
 258                                               sb->buf,
 259                                               0);
 260         sb->buf = NULL;
 261     }
 262 }
 263 
 264 /*
 265  * Clear out the streamBuffer.  This just invalidates the data in the buffer.
 266  */
 267 static void clearStreamBuffer(streamBufferPtr sb) {
 268     sb->bufferOffset = NO_DATA;
 269 }
 270 
 271 /*************************** end StreamBuffer definition *************/
 272 
 273 /*************************** Pixel Buffer definition ******************/
 274 
 275 typedef struct pixelBufferStruct {
 276     jobject hpixelObject;   // Usually a DataBuffer bank as a byte array
 277     unsigned int byteBufferLength;
 278     union pixptr {
 279         INT32         *ip;  // Pinned buffer pointer, as 32-bit ints
 280         unsigned char *bp;  // Pinned buffer pointer, as bytes
 281     } buf;
 282 } pixelBuffer, *pixelBufferPtr;
 283 
 284 /*
 285  * Initialize a freshly allocated PixelBuffer.  All fields are simply
 286  * set to NULL, as we have no idea what size buffer we will need.
 287  */
 288 static void initPixelBuffer(pixelBufferPtr pb) {
 289     pb->hpixelObject = NULL;
 290     pb->byteBufferLength = 0;
 291     pb->buf.ip = NULL;
 292 }
 293 
 294 /*
 295  * Set the pixelBuffer to use the given buffer, acquiring a new global
 296  * reference for it.  Returns OK on success, NOT_OK on failure.
 297  */
 298 static int setPixelBuffer(JNIEnv *env, pixelBufferPtr pb, jobject obj) {
 299     pb->hpixelObject = (*env)->NewGlobalRef(env, obj);
 300     if (pb->hpixelObject == NULL) {
 301         JNU_ThrowByName( env,
 302                          "java/lang/OutOfMemoryError",
 303                          "Setting Pixel Buffer");
 304         return NOT_OK;
 305     }
 306     pb->byteBufferLength = (*env)->GetArrayLength(env, pb->hpixelObject);
 307     return OK;
 308 }
 309 
 310 // Forward reference
 311 static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb);
 312 
 313 /*
 314  * Resets a pixel buffer to its initial state.  Unpins any pixel buffer,
 315  * releases the global reference, and resets fields to NULL.  Use this
 316  * method to dispose the object as well (there is no destroyPixelBuffer).
 317  */
 318 static void resetPixelBuffer(JNIEnv *env, pixelBufferPtr pb) {
 319     if (pb->hpixelObject != NULL) {
 320         unpinPixelBuffer(env, pb);
 321         (*env)->DeleteGlobalRef(env, pb->hpixelObject);
 322         pb->hpixelObject = NULL;
 323         pb->byteBufferLength = 0;
 324     }
 325 }
 326 
 327 /*
 328  * Pins the data buffer.  Returns OK on success, NOT_OK on failure.
 329  */
 330 static int pinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) {
 331     if (pb->hpixelObject != NULL) {
 332         assert(pb->buf.ip == NULL);
 333         pb->buf.bp = (unsigned char *)(*env)->GetPrimitiveArrayCritical
 334             (env, pb->hpixelObject, NULL);
 335         if (pb->buf.bp == NULL) {
 336             return NOT_OK;
 337         }
 338     }
 339     return OK;
 340 }
 341 
 342 /*
 343  * Unpins the data buffer.
 344  */
 345 static void unpinPixelBuffer(JNIEnv *env, pixelBufferPtr pb) {
 346 
 347     if (pb->buf.ip != NULL) {
 348         assert(pb->hpixelObject != NULL);
 349         (*env)->ReleasePrimitiveArrayCritical(env,
 350                                               pb->hpixelObject,
 351                                               pb->buf.ip,
 352                                               0);
 353         pb->buf.ip = NULL;
 354     }
 355 }
 356 
 357 /********************* end PixelBuffer definition *******************/
 358 
 359 /********************* ImageIOData definition ***********************/
 360 
 361 #define MAX_BANDS 4
 362 #define JPEG_BAND_SIZE 8
 363 #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)
 366 
 367 /* The number of possible incoming values to be scaled. */
 368 #define NUM_INPUT_VALUES (1 << 16)
 369 
 370 /*
 371  * The principal imageioData object, opaque to I/O direction.
 372  * Each JPEGImageReader will have associated with it a
 373  * jpeg_decompress_struct, and similarly each JPEGImageWriter will
 374  * have associated with it a jpeg_compress_struct.  In order to
 375  * ensure that these associations persist from one native call to
 376  * the next, and to provide a central locus of imageio-specific
 377  * data, we define an imageioData struct containing references
 378  * to the Java object and the IJG structs.  The functions
 379  * that manipulate these objects know whether input or output is being
 380  * performed and therefore know how to manipulate the contents correctly.
 381  * If for some reason they don't, the direction can be determined by
 382  * checking the is_decompressor field of the jpegObj.
 383  * In order for lower level code to determine a
 384  * 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 pointer
 386  * to the imageIOData object.  Maintenance of this pointer is performed
 387  * exclusively within the following access functions.  If you
 388  * change that, you run the risk of dangling pointers.
 389  */
 390 typedef struct imageIODataStruct {
 391     j_common_ptr jpegObj;     // Either struct is fine
 392     jobject imageIOobj;       // A JPEGImageReader or a JPEGImageWriter
 393 
 394     streamBuffer streamBuf;   // Buffer for the stream
 395     pixelBuffer pixelBuf;     // Buffer for pixels
 396 
 397     jboolean abortFlag;       // Passed down from Java abort method
 398 } imageIOData, *imageIODataPtr;
 399 
 400 /*
 401  * Allocate and initialize a new imageIOData object to associate the
 402  * jpeg object and the Java object.  Returns a pointer to the new object
 403  * on success, NULL on failure.
 404  */
 405 static imageIODataPtr initImageioData (JNIEnv *env,
 406                                        j_common_ptr cinfo,
 407                                        jobject obj) {
 408 
 409     imageIODataPtr data = (imageIODataPtr) malloc (sizeof(imageIOData));
 410     if (data == NULL) {
 411         return NULL;
 412     }
 413 
 414     data->jpegObj = cinfo;
 415     cinfo->client_data = data;
 416 
 417 #ifdef DEBUG_IIO_JPEG
 418     printf("new structures: data is %p, cinfo is %p\n", data, cinfo);
 419 #endif
 420 
 421     data->imageIOobj = (*env)->NewWeakGlobalRef(env, obj);
 422     if (data->imageIOobj == NULL) {
 423         free (data);
 424         return NULL;
 425     }
 426     if (initStreamBuffer(env, &data->streamBuf) == NOT_OK) {
 427         (*env)->DeleteWeakGlobalRef(env, data->imageIOobj);
 428         free (data);
 429         return NULL;
 430     }
 431     initPixelBuffer(&data->pixelBuf);
 432 
 433     data->abortFlag = JNI_FALSE;
 434 
 435     return data;
 436 }
 437 
 438 /*
 439  * Resets the imageIOData object to its initial state, as though
 440  * it had just been allocated and initialized.
 441  */
 442 static void resetImageIOData(JNIEnv *env, imageIODataPtr data) {
 443     resetStreamBuffer(env, &data->streamBuf);
 444     resetPixelBuffer(env, &data->pixelBuf);
 445     data->abortFlag = JNI_FALSE;
 446 }
 447 
 448 /*
 449  * Releases all resources held by this object and its subobjects,
 450  * frees the object, and returns the jpeg object.  This method must
 451  * be called to avoid leaking global references.
 452  * Note that the jpeg object is not freed or destroyed, as that is
 453  * the client's responsibility, although the client_data field is
 454  * cleared.
 455  */
 456 static j_common_ptr destroyImageioData(JNIEnv *env, imageIODataPtr data) {
 457     j_common_ptr ret = data->jpegObj;
 458     (*env)->DeleteWeakGlobalRef(env, data->imageIOobj);
 459     destroyStreamBuffer(env, &data->streamBuf);
 460     resetPixelBuffer(env, &data->pixelBuf);
 461     ret->client_data = NULL;
 462     free(data);
 463     return ret;
 464 }
 465 
 466 /******************** end ImageIOData definition ***********************/
 467 
 468 /******************** Java array pinning and unpinning *****************/
 469 
 470 /* We use Get/ReleasePrimitiveArrayCritical functions to avoid
 471  * 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 around
 476  *   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  */
 484 
 485 /*
 486  * Release (unpin) all the arrays in use during a read.
 487  */
 488 static void RELEASE_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET *next_byte)
 489 {
 490     unpinStreamBuffer(env, &data->streamBuf, next_byte);
 491 
 492     unpinPixelBuffer(env, &data->pixelBuf);
 493 
 494 }
 495 
 496 /*
 497  * Get (pin) all the arrays in use during a read.
 498  */
 499 static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte) {
 500     if (pinStreamBuffer(env, &data->streamBuf, next_byte) == NOT_OK) {
 501         return NOT_OK;
 502     }
 503 
 504     if (pinPixelBuffer(env, &data->pixelBuf) == NOT_OK) {
 505         RELEASE_ARRAYS(env, data, *next_byte);
 506         return NOT_OK;
 507     }
 508     return OK;
 509 }
 510 
 511 /****** end of Java array pinning and unpinning ***********/
 512 
 513 /****** Error Handling *******/
 514 
 515 /*
 516  * Set up error handling to use setjmp/longjmp.  This is the third such
 517  * setup, as both the AWT jpeg decoder and the com.sun... JPEG classes
 518  * setup thier own.  Ultimately these should be integrated, as they all
 519  * do pretty much the same thing.
 520  */
 521 
 522 struct sun_jpeg_error_mgr {
 523   struct jpeg_error_mgr pub;    /* "public" fields */
 524 
 525   jmp_buf setjmp_buffer;        /* for return to caller */
 526 };
 527 
 528 typedef struct sun_jpeg_error_mgr * sun_jpeg_error_ptr;
 529 
 530 /*
 531  * Here's the routine that will replace the standard error_exit method:
 532  */
 533 
 534 METHODDEF(void)
 535 sun_jpeg_error_exit (j_common_ptr cinfo)
 536 {
 537   /* cinfo->err really points to a sun_jpeg_error_mgr struct */
 538   sun_jpeg_error_ptr myerr = (sun_jpeg_error_ptr) cinfo->err;
 539 
 540   /* For Java, we will format the message and put it in the error we throw. */
 541 
 542   /* Return control to the setjmp point */
 543   longjmp(myerr->setjmp_buffer, 1);
 544 }
 545 
 546 /*
 547  * Error Message handling
 548  *
 549  * This overrides the output_message method to send JPEG messages
 550  *
 551  */
 552 
 553 METHODDEF(void)
 554 sun_jpeg_output_message (j_common_ptr cinfo)
 555 {
 556   char buffer[JMSG_LENGTH_MAX];
 557   jstring string;
 558   imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
 559   JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 560   jobject theObject;
 561 
 562   /* Create the message */
 563   (*cinfo->err->format_message) (cinfo, buffer);
 564 
 565   // Create a new java string from the message
 566   string = (*env)->NewStringUTF(env, buffer);
 567   CHECK_NULL(string);
 568 
 569   theObject = data->imageIOobj;
 570 
 571   if (cinfo->is_decompressor) {
 572       (*env)->CallVoidMethod(env, theObject,
 573                              JPEGImageReader_warningWithMessageID,
 574                              string);
 575   } else {
 576       (*env)->CallVoidMethod(env, theObject,
 577                              JPEGImageWriter_warningWithMessageID,
 578                              string);
 579   }
 580 }
 581 
 582 /* End of verbatim copy from jpegdecoder.c */
 583 
 584 /*************** end of error handling *********************/
 585 
 586 /*************** Shared utility code ***********************/
 587 
 588 static void imageio_set_stream(JNIEnv *env,
 589                                j_common_ptr cinfo,
 590                                imageIODataPtr data,
 591                                jobject io){
 592     streamBufferPtr sb;
 593     sun_jpeg_error_ptr jerr;
 594 
 595     sb = &data->streamBuf;
 596 
 597     resetStreamBuffer(env, sb);  // Removes any old stream
 598 
 599     /* Now we need a new weak global reference for the I/O provider */
 600     if (io != NULL) { // Fix for 4411955
 601         sb->ioRef = (*env)->NewWeakGlobalRef(env, io);
 602         CHECK_NULL(sb->ioRef);
 603     }
 604 
 605     /* And finally reset state */
 606     data->abortFlag = JNI_FALSE;
 607 
 608     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
 609     jerr = (sun_jpeg_error_ptr) cinfo->err;
 610 
 611     if (setjmp(jerr->setjmp_buffer)) {
 612         /* If we get here, the JPEG code has signaled an error
 613            while aborting. */
 614         if (!(*env)->ExceptionOccurred(env)) {
 615             char buffer[JMSG_LENGTH_MAX];
 616             (*cinfo->err->format_message) (cinfo,
 617                                            buffer);
 618             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
 619         }
 620         return;
 621     }
 622 
 623     jpeg_abort(cinfo);  // Frees any markers, but not tables
 624 
 625 }
 626 
 627 static void imageio_reset(JNIEnv *env,
 628                           j_common_ptr cinfo,
 629                           imageIODataPtr data) {
 630     sun_jpeg_error_ptr jerr;
 631 
 632     resetImageIOData(env, data);  // Mapping to jpeg object is retained.
 633 
 634     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
 635     jerr = (sun_jpeg_error_ptr) cinfo->err;
 636 
 637     if (setjmp(jerr->setjmp_buffer)) {
 638         /* If we get here, the JPEG code has signaled an error
 639            while aborting. */
 640         if (!(*env)->ExceptionOccurred(env)) {
 641             char buffer[JMSG_LENGTH_MAX];
 642             (*cinfo->err->format_message) (cinfo, buffer);
 643             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
 644         }
 645         return;
 646     }
 647 
 648     jpeg_abort(cinfo);  // Does not reset tables
 649 
 650 }
 651 
 652 static void imageio_dispose(j_common_ptr info) {
 653 
 654     if (info != NULL) {
 655         free(info->err);
 656         info->err = NULL;
 657         if (info->is_decompressor) {
 658             j_decompress_ptr dinfo = (j_decompress_ptr) info;
 659             free(dinfo->src);
 660             dinfo->src = NULL;
 661         } else {
 662             j_compress_ptr cinfo = (j_compress_ptr) info;
 663             free(cinfo->dest);
 664             cinfo->dest = NULL;
 665         }
 666         jpeg_destroy(info);
 667         free(info);
 668     }
 669 }
 670 
 671 static void imageio_abort(JNIEnv *env, jobject this,
 672                           imageIODataPtr data) {
 673     data->abortFlag = JNI_TRUE;
 674 }
 675 
 676 static int setQTables(JNIEnv *env,
 677                       j_common_ptr cinfo,
 678                       jobjectArray qtables,
 679                       boolean write) {
 680     jsize qlen;
 681     jobject table;
 682     jintArray qdata;
 683     jint *qdataBody;
 684     JQUANT_TBL *quant_ptr;
 685     int i, j;
 686     j_compress_ptr comp;
 687     j_decompress_ptr decomp;
 688 
 689     qlen = (*env)->GetArrayLength(env, qtables);
 690 #ifdef DEBUG_IIO_JPEG
 691     printf("in setQTables, qlen = %d, write is %d\n", qlen, write);
 692 #endif
 693     if (qlen > NUM_QUANT_TBLS) {
 694         /* Ignore extra qunterization tables. */
 695         qlen = NUM_QUANT_TBLS;
 696     }
 697     for (i = 0; i < qlen; i++) {
 698         table = (*env)->GetObjectArrayElement(env, qtables, i);
 699         CHECK_NULL_RETURN(table, 0);
 700         qdata = (*env)->GetObjectField(env, table, JPEGQTable_tableID);
 701         qdataBody = (*env)->GetPrimitiveArrayCritical(env, qdata, NULL);
 702 
 703         if (cinfo->is_decompressor) {
 704             decomp = (j_decompress_ptr) cinfo;
 705             if (decomp->quant_tbl_ptrs[i] == NULL) {
 706                 decomp->quant_tbl_ptrs[i] =
 707                     jpeg_alloc_quant_table(cinfo);
 708             }
 709             quant_ptr = decomp->quant_tbl_ptrs[i];
 710         } else {
 711             comp = (j_compress_ptr) cinfo;
 712             if (comp->quant_tbl_ptrs[i] == NULL) {
 713                 comp->quant_tbl_ptrs[i] =
 714                     jpeg_alloc_quant_table(cinfo);
 715             }
 716             quant_ptr = comp->quant_tbl_ptrs[i];
 717         }
 718 
 719         for (j = 0; j < 64; j++) {
 720             quant_ptr->quantval[j] = (UINT16)qdataBody[j];
 721         }
 722         quant_ptr->sent_table = !write;
 723         (*env)->ReleasePrimitiveArrayCritical(env,
 724                                               qdata,
 725                                               qdataBody,
 726                                               0);
 727     }
 728     return qlen;
 729 }
 730 
 731 static boolean setHuffTable(JNIEnv *env,
 732                          JHUFF_TBL *huff_ptr,
 733                          jobject table) {
 734 
 735     jshortArray huffLens;
 736     jshortArray huffValues;
 737     jshort *hlensBody, *hvalsBody;
 738     jsize hlensLen, hvalsLen;
 739     int i;
 740 
 741     // lengths
 742     huffLens = (*env)->GetObjectField(env,
 743                                       table,
 744                                       JPEGHuffmanTable_lengthsID);
 745     hlensLen = (*env)->GetArrayLength(env, huffLens);
 746     hlensBody = (*env)->GetShortArrayElements(env,
 747                                               huffLens,
 748                                               NULL);
 749     CHECK_NULL_RETURN(hlensBody, FALSE);
 750 
 751     if (hlensLen > 16) {
 752         /* Ignore extra elements of bits array. Only 16 elements can be
 753            stored. 0-th element is not used. (see jpeglib.h, line 107)  */
 754         hlensLen = 16;
 755     }
 756     for (i = 1; i <= hlensLen; i++) {
 757         huff_ptr->bits[i] = (UINT8)hlensBody[i-1];
 758     }
 759     (*env)->ReleaseShortArrayElements(env,
 760                                       huffLens,
 761                                       hlensBody,
 762                                       JNI_ABORT);
 763     // values
 764     huffValues = (*env)->GetObjectField(env,
 765                                         table,
 766                                         JPEGHuffmanTable_valuesID);
 767     hvalsLen = (*env)->GetArrayLength(env, huffValues);
 768     hvalsBody = (*env)->GetShortArrayElements(env,
 769                                               huffValues,
 770                                               NULL);
 771     CHECK_NULL_RETURN(hvalsBody, FALSE);
 772 
 773     if (hvalsLen > 256) {
 774         /* Ignore extra elements of hufval array. Only 256 elements
 775            can be stored. (see jpeglib.h, line 109)                  */
 776         hlensLen = 256;
 777     }
 778     for (i = 0; i < hvalsLen; i++) {
 779         huff_ptr->huffval[i] = (UINT8)hvalsBody[i];
 780     }
 781     (*env)->ReleaseShortArrayElements(env,
 782                                       huffValues,
 783                                       hvalsBody,
 784                                       JNI_ABORT);
 785     return TRUE;
 786 }
 787 
 788 static int setHTables(JNIEnv *env,
 789                       j_common_ptr cinfo,
 790                       jobjectArray DCHuffmanTables,
 791                       jobjectArray ACHuffmanTables,
 792                       boolean write) {
 793     int i;
 794     jobject table;
 795     JHUFF_TBL *huff_ptr;
 796     j_compress_ptr comp;
 797     j_decompress_ptr decomp;
 798     jsize hlen = (*env)->GetArrayLength(env, DCHuffmanTables);
 799 
 800     if (hlen > NUM_HUFF_TBLS) {
 801         /* Ignore extra DC huffman tables. */
 802         hlen = NUM_HUFF_TBLS;
 803     }
 804     for (i = 0; i < hlen; i++) {
 805         if (cinfo->is_decompressor) {
 806             decomp = (j_decompress_ptr) cinfo;
 807             if (decomp->dc_huff_tbl_ptrs[i] == NULL) {
 808                 decomp->dc_huff_tbl_ptrs[i] =
 809                     jpeg_alloc_huff_table(cinfo);
 810             }
 811             huff_ptr = decomp->dc_huff_tbl_ptrs[i];
 812         } else {
 813             comp = (j_compress_ptr) cinfo;
 814             if (comp->dc_huff_tbl_ptrs[i] == NULL) {
 815                 comp->dc_huff_tbl_ptrs[i] =
 816                     jpeg_alloc_huff_table(cinfo);
 817             }
 818             huff_ptr = comp->dc_huff_tbl_ptrs[i];
 819         }
 820         table = (*env)->GetObjectArrayElement(env, DCHuffmanTables, i);
 821         if (table == NULL || !setHuffTable(env, huff_ptr, table)) {
 822             return 0;
 823         }
 824         huff_ptr->sent_table = !write;
 825     }
 826     hlen = (*env)->GetArrayLength(env, ACHuffmanTables);
 827     if (hlen > NUM_HUFF_TBLS) {
 828         /* Ignore extra AC huffman tables. */
 829         hlen = NUM_HUFF_TBLS;
 830     }
 831     for (i = 0; i < hlen; i++) {
 832         if (cinfo->is_decompressor) {
 833             decomp = (j_decompress_ptr) cinfo;
 834             if (decomp->ac_huff_tbl_ptrs[i] == NULL) {
 835                 decomp->ac_huff_tbl_ptrs[i] =
 836                     jpeg_alloc_huff_table(cinfo);
 837             }
 838             huff_ptr = decomp->ac_huff_tbl_ptrs[i];
 839         } else {
 840             comp = (j_compress_ptr) cinfo;
 841             if (comp->ac_huff_tbl_ptrs[i] == NULL) {
 842                 comp->ac_huff_tbl_ptrs[i] =
 843                     jpeg_alloc_huff_table(cinfo);
 844             }
 845             huff_ptr = comp->ac_huff_tbl_ptrs[i];
 846         }
 847         table = (*env)->GetObjectArrayElement(env, ACHuffmanTables, i);
 848         if(table == NULL || !setHuffTable(env, huff_ptr, table)) {
 849             return 0;
 850         }
 851         huff_ptr->sent_table = !write;
 852     }
 853     return hlen;
 854 }
 855 
 856 
 857 /*************** end of shared utility code ****************/
 858 
 859 /********************** Reader Support **************************/
 860 
 861 /********************** Source Management ***********************/
 862 
 863 /*
 864  * INPUT HANDLING:
 865  *
 866  * The JPEG library's input management is defined by the jpeg_source_mgr
 867  * structure which contains two fields to convey the information in the
 868  * buffer and 5 methods which perform all buffer management.  The library
 869  * defines a standard input manager that uses stdio for obtaining compressed
 870  * jpeg data, but here we need to use Java to get our data.
 871  *
 872  * We use the library jpeg_source_mgr but our own routines that access
 873  * imageio-specific information in the imageIOData structure.
 874  */
 875 
 876 /*
 877  * Initialize source.  This is called by jpeg_read_header() before any
 878  * data is actually read.  Unlike init_destination(), it may leave
 879  * bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
 880  * will occur immediately).
 881  */
 882 
 883 GLOBAL(void)
 884 imageio_init_source(j_decompress_ptr cinfo)
 885 {
 886     struct jpeg_source_mgr *src = cinfo->src;
 887     src->next_input_byte = NULL;
 888     src->bytes_in_buffer = 0;
 889 }
 890 
 891 /*
 892  * This is called whenever bytes_in_buffer has reached zero and more
 893  * data is wanted.  In typical applications, it should read fresh data
 894  * into the buffer (ignoring the current state of next_input_byte and
 895  * bytes_in_buffer), reset the pointer & count to the start of the
 896  * buffer, and return TRUE indicating that the buffer has been reloaded.
 897  * It is not necessary to fill the buffer entirely, only to obtain at
 898  * least one more byte.  bytes_in_buffer MUST be set to a positive value
 899  * if TRUE is returned.  A FALSE return should only be used when I/O
 900  * suspension is desired (this mode is discussed in the next section).
 901  */
 902 /*
 903  * Note that with I/O suspension turned on, this procedure should not
 904  * do any work since the JPEG library has a very simple backtracking
 905  * mechanism which relies on the fact that the buffer will be filled
 906  * only when it has backed out to the top application level.  When
 907  * suspendable is turned on, imageio_fill_suspended_buffer will
 908  * do the actual work of filling the buffer.
 909  */
 910 
 911 GLOBAL(boolean)
 912 imageio_fill_input_buffer(j_decompress_ptr cinfo)
 913 {
 914     struct jpeg_source_mgr *src = cinfo->src;
 915     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
 916     streamBufferPtr sb = &data->streamBuf;
 917     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 918     int ret;
 919     jobject input = NULL;
 920 
 921     /* This is where input suspends */
 922     if (sb->suspendable) {
 923         return FALSE;
 924     }
 925 
 926 #ifdef DEBUG_IIO_JPEG
 927     printf("Filling input buffer, remaining skip is %ld, ",
 928            sb->remaining_skip);
 929     printf("Buffer length is %d\n", sb->bufferLength);
 930 #endif
 931 
 932     /*
 933      * Definitively skips.  Could be left over if we tried to skip
 934      * more than a buffer's worth but suspended when getting the next
 935      * buffer.  Now we aren't suspended, so we can catch up.
 936      */
 937     if (sb->remaining_skip) {
 938         src->skip_input_data(cinfo, 0);
 939     }
 940 
 941     /*
 942      * Now fill a complete buffer, or as much of one as the stream
 943      * will give us if we are near the end.
 944      */
 945     RELEASE_ARRAYS(env, data, src->next_input_byte);
 946 
 947     GET_IO_REF(input);
 948 
 949     ret = (*env)->CallIntMethod(env,
 950                                 input,
 951                                 JPEGImageReader_readInputDataID,
 952                                 sb->hstreamBuffer, 0,
 953                                 sb->bufferLength);
 954     if ((ret > 0) && ((unsigned int)ret > sb->bufferLength)) {
 955          ret = sb->bufferLength;
 956     }
 957     if ((*env)->ExceptionOccurred(env)
 958         || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
 959             cinfo->err->error_exit((j_common_ptr) cinfo);
 960     }
 961 
 962 #ifdef DEBUG_IIO_JPEG
 963       printf("Buffer filled. ret = %d\n", ret);
 964 #endif
 965     /*
 966      * If we have reached the end of the stream, then the EOI marker
 967      * is missing.  We accept such streams but generate a warning.
 968      * The image is likely to be corrupted, though everything through
 969      * the end of the last complete MCU should be usable.
 970      */
 971     if (ret <= 0) {
 972         jobject reader = data->imageIOobj;
 973 #ifdef DEBUG_IIO_JPEG
 974       printf("YO! Early EOI! ret = %d\n", ret);
 975 #endif
 976         RELEASE_ARRAYS(env, data, src->next_input_byte);
 977         (*env)->CallVoidMethod(env, reader,
 978                                JPEGImageReader_warningOccurredID,
 979                                READ_NO_EOI);
 980         if ((*env)->ExceptionOccurred(env)
 981             || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
 982             cinfo->err->error_exit((j_common_ptr) cinfo);
 983         }
 984 
 985         sb->buf[0] = (JOCTET) 0xFF;
 986         sb->buf[1] = (JOCTET) JPEG_EOI;
 987         ret = 2;
 988     }
 989 
 990     src->next_input_byte = sb->buf;
 991     src->bytes_in_buffer = ret;
 992 
 993     return TRUE;
 994 }
 995 
 996 /*
 997  * With I/O suspension turned on, the JPEG library requires that all
 998  * buffer filling be done at the top application level, using this
 999  * function.  Due to the way that backtracking works, this procedure
1000  * saves all of the data that was left in the buffer when suspension
1001  * occurred and read new data only at the end.
1002  */
1003 
1004 GLOBAL(void)
1005 imageio_fill_suspended_buffer(j_decompress_ptr cinfo)
1006 {
1007     struct jpeg_source_mgr *src = cinfo->src;
1008     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
1009     streamBufferPtr sb = &data->streamBuf;
1010     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
1011     jint ret;
1012     size_t offset, buflen;
1013     jobject input = NULL;
1014 
1015     /*
1016      * The original (jpegdecoder.c) had code here that called
1017      * InputStream.available and just returned if the number of bytes
1018      * available was less than any remaining skip.  Presumably this was
1019      * to avoid blocking, although the benefit was unclear, as no more
1020      * decompression can take place until more data is available, so
1021      * the code would block on input a little further along anyway.
1022      * ImageInputStreams don't have an available method, so we'll just
1023      * block in the skip if we have to.
1024      */
1025 
1026     if (sb->remaining_skip) {
1027         src->skip_input_data(cinfo, 0);
1028     }
1029 
1030     /* Save the data currently in the buffer */
1031     offset = src->bytes_in_buffer;
1032     if (src->next_input_byte > sb->buf) {
1033         memcpy(sb->buf, src->next_input_byte, offset);
1034     }
1035 
1036 
1037     RELEASE_ARRAYS(env, data, src->next_input_byte);
1038 
1039     GET_IO_REF(input);
1040 
1041     buflen = sb->bufferLength - offset;
1042     if (buflen <= 0) {
1043         if (!GET_ARRAYS(env, data, &(src->next_input_byte))) {
1044             cinfo->err->error_exit((j_common_ptr) cinfo);
1045         }
1046         return;
1047     }
1048 
1049     ret = (*env)->CallIntMethod(env, input,
1050                                 JPEGImageReader_readInputDataID,
1051                                 sb->hstreamBuffer,
1052                                 offset, buflen);
1053     if ((ret > 0) && ((unsigned int)ret > buflen)) ret = buflen;
1054     if ((*env)->ExceptionOccurred(env)
1055         || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
1056         cinfo->err->error_exit((j_common_ptr) cinfo);
1057     }
1058     /*
1059      * If we have reached the end of the stream, then the EOI marker
1060      * is missing.  We accept such streams but generate a warning.
1061      * The image is likely to be corrupted, though everything through
1062      * the end of the last complete MCU should be usable.
1063      */
1064     if (ret <= 0) {
1065         jobject reader = data->imageIOobj;
1066         RELEASE_ARRAYS(env, data, src->next_input_byte);
1067         (*env)->CallVoidMethod(env, reader,
1068                                JPEGImageReader_warningOccurredID,
1069                                READ_NO_EOI);
1070         if ((*env)->ExceptionOccurred(env)
1071             || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
1072             cinfo->err->error_exit((j_common_ptr) cinfo);
1073         }
1074 
1075         sb->buf[offset] = (JOCTET) 0xFF;
1076         sb->buf[offset + 1] = (JOCTET) JPEG_EOI;
1077         ret = 2;
1078     }
1079 
1080     src->next_input_byte = sb->buf;
1081     src->bytes_in_buffer = ret + offset;
1082 
1083     return;
1084 }
1085 
1086 /*
1087  * Skip num_bytes worth of data.  The buffer pointer and count are
1088  * advanced over num_bytes input bytes, using the input stream
1089  * skipBytes method if the skip is greater than the number of bytes
1090  * in the buffer.  This is used to skip over a potentially large amount of
1091  * uninteresting data (such as an APPn marker).  bytes_in_buffer will be
1092  * zero on return if the skip is larger than the current contents of the
1093  * buffer.
1094  *
1095  * A negative skip count is treated as a no-op.  A zero skip count
1096  * skips any remaining skip from a previous skip while suspended.
1097  *
1098  * Note that with I/O suspension turned on, this procedure does not
1099  * call skipBytes since the JPEG library has a very simple backtracking
1100  * mechanism which relies on the fact that the application level has
1101  * exclusive control over actual I/O.
1102  */
1103 
1104 GLOBAL(void)
1105 imageio_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
1106 {
1107     struct jpeg_source_mgr *src = cinfo->src;
1108     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
1109     streamBufferPtr sb = &data->streamBuf;
1110     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
1111     jlong ret;
1112     jobject reader;
1113     jobject input = NULL;
1114 
1115     if (num_bytes < 0) {
1116         return;
1117     }
1118     num_bytes += sb->remaining_skip;
1119     sb->remaining_skip = 0;
1120 
1121     /* First the easy case where we are skipping <= the current contents. */
1122     ret = src->bytes_in_buffer;
1123     if (ret >= num_bytes) {
1124         src->next_input_byte += num_bytes;
1125         src->bytes_in_buffer -= num_bytes;
1126         return;
1127     }
1128 
1129     /*
1130      * We are skipping more than is in the buffer.  We empty the buffer and,
1131      * if we aren't suspended, call the Java skipBytes method.  We always
1132      * leave the buffer empty, to be filled by either fill method above.
1133      */
1134     src->bytes_in_buffer = 0;
1135     src->next_input_byte = sb->buf;
1136 
1137     num_bytes -= (long)ret;
1138     if (sb->suspendable) {
1139         sb->remaining_skip = num_bytes;
1140         return;
1141     }
1142 
1143     RELEASE_ARRAYS(env, data, src->next_input_byte);
1144 
1145     GET_IO_REF(input);
1146 
1147     ret = (*env)->CallLongMethod(env,
1148                                  input,
1149                                  JPEGImageReader_skipInputBytesID,
1150                                  (jlong) num_bytes);
1151     if ((*env)->ExceptionOccurred(env)
1152         || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
1153             cinfo->err->error_exit((j_common_ptr) cinfo);
1154     }
1155 
1156     /*
1157      * If we have reached the end of the stream, then the EOI marker
1158      * is missing.  We accept such streams but generate a warning.
1159      * The image is likely to be corrupted, though everything through
1160      * the end of the last complete MCU should be usable.
1161      */
1162     if (ret <= 0) {
1163         reader = data->imageIOobj;
1164         RELEASE_ARRAYS(env, data, src->next_input_byte);
1165         (*env)->CallVoidMethod(env,
1166                                reader,
1167                                JPEGImageReader_warningOccurredID,
1168                                READ_NO_EOI);
1169 
1170         if ((*env)->ExceptionOccurred(env)
1171             || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
1172                 cinfo->err->error_exit((j_common_ptr) cinfo);
1173         }
1174         sb->buf[0] = (JOCTET) 0xFF;
1175         sb->buf[1] = (JOCTET) JPEG_EOI;
1176         src->bytes_in_buffer = 2;
1177         src->next_input_byte = sb->buf;
1178     }
1179 }
1180 
1181 /*
1182  * Terminate source --- called by jpeg_finish_decompress() after all
1183  * data for an image has been read.  In our case pushes back any
1184  * remaining data, as it will be for another image and must be available
1185  * for java to find out that there is another image.  Also called if
1186  * reseting state after reading a tables-only image.
1187  */
1188 
1189 GLOBAL(void)
1190 imageio_term_source(j_decompress_ptr cinfo)
1191 {
1192     // To pushback, just seek back by src->bytes_in_buffer
1193     struct jpeg_source_mgr *src = cinfo->src;
1194     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
1195     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
1196     jobject reader = data->imageIOobj;
1197     if (src->bytes_in_buffer > 0) {
1198          RELEASE_ARRAYS(env, data, src->next_input_byte);
1199          (*env)->CallVoidMethod(env,
1200                                 reader,
1201                                 JPEGImageReader_pushBackID,
1202                                 src->bytes_in_buffer);
1203 
1204          if ((*env)->ExceptionOccurred(env)
1205              || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
1206              cinfo->err->error_exit((j_common_ptr) cinfo);
1207          }
1208          src->bytes_in_buffer = 0;
1209          //src->next_input_byte = sb->buf;
1210     }
1211 }
1212 
1213 /********************* end of source manager ******************/
1214 
1215 /********************* ICC profile support ********************/
1216 /*
1217  * The following routines are modified versions of the ICC
1218  * profile support routines available from the IJG website.
1219  * The originals were written by Todd Newman
1220  * <tdn@eccentric.esd.sgi.com> and modified by Tom Lane for
1221  * the IJG.  They are further modified to fit in the context
1222  * of the imageio JPEG plug-in.
1223  */
1224 
1225 /*
1226  * Since an ICC profile can be larger than the maximum size of a JPEG marker
1227  * (64K), we need provisions to split it into multiple markers.  The format
1228  * defined by the ICC specifies one or more APP2 markers containing the
1229  * following data:
1230  *      Identifying string      ASCII "ICC_PROFILE\0"  (12 bytes)
1231  *      Marker sequence number  1 for first APP2, 2 for next, etc (1 byte)
1232  *      Number of markers       Total number of APP2's used (1 byte)
1233  *      Profile data            (remainder of APP2 data)
1234  * Decoders should use the marker sequence numbers to reassemble the profile,
1235  * rather than assuming that the APP2 markers appear in the correct sequence.
1236  */
1237 
1238 #define ICC_MARKER  (JPEG_APP0 + 2)     /* JPEG marker code for ICC */
1239 #define ICC_OVERHEAD_LEN  14            /* size of non-profile data in APP2 */
1240 #define MAX_BYTES_IN_MARKER  65533      /* maximum data len of a JPEG marker */
1241 #define MAX_DATA_BYTES_IN_ICC_MARKER  (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
1242 
1243 
1244 /*
1245  * Handy subroutine to test whether a saved marker is an ICC profile marker.
1246  */
1247 
1248 static boolean
1249 marker_is_icc (jpeg_saved_marker_ptr marker)
1250 {
1251   return
1252     marker->marker == ICC_MARKER &&
1253     marker->data_length >= ICC_OVERHEAD_LEN &&
1254     /* verify the identifying string */
1255     GETJOCTET(marker->data[0]) == 0x49 &&
1256     GETJOCTET(marker->data[1]) == 0x43 &&
1257     GETJOCTET(marker->data[2]) == 0x43 &&
1258     GETJOCTET(marker->data[3]) == 0x5F &&
1259     GETJOCTET(marker->data[4]) == 0x50 &&
1260     GETJOCTET(marker->data[5]) == 0x52 &&
1261     GETJOCTET(marker->data[6]) == 0x4F &&
1262     GETJOCTET(marker->data[7]) == 0x46 &&
1263     GETJOCTET(marker->data[8]) == 0x49 &&
1264     GETJOCTET(marker->data[9]) == 0x4C &&
1265     GETJOCTET(marker->data[10]) == 0x45 &&
1266     GETJOCTET(marker->data[11]) == 0x0;
1267 }
1268 
1269 /*
1270  * See if there was an ICC profile in the JPEG file being read;
1271  * if so, reassemble and return the profile data as a new Java byte array.
1272  * If there was no ICC profile, return NULL.
1273  *
1274  * If the file contains invalid ICC APP2 markers, we throw an IIOException
1275  * with an appropriate message.
1276  */
1277 
1278 jbyteArray
1279 read_icc_profile (JNIEnv *env, j_decompress_ptr cinfo)
1280 {
1281     jpeg_saved_marker_ptr marker;
1282     int num_markers = 0;
1283     int num_found_markers = 0;
1284     int seq_no;
1285     JOCTET *icc_data;
1286     JOCTET *dst_ptr;
1287     unsigned int total_length;
1288 #define MAX_SEQ_NO  255         // sufficient since marker numbers are bytes
1289     jpeg_saved_marker_ptr icc_markers[MAX_SEQ_NO + 1];
1290     int first;         // index of the first marker in the icc_markers array
1291     int last;          // index of the last marker in the icc_markers array
1292     jbyteArray data = NULL;
1293 
1294     /* This first pass over the saved markers discovers whether there are
1295      * any ICC markers and verifies the consistency of the marker numbering.
1296      */
1297 
1298     for (seq_no = 0; seq_no <= MAX_SEQ_NO; seq_no++)
1299         icc_markers[seq_no] = NULL;
1300 
1301 
1302     for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
1303         if (marker_is_icc(marker)) {
1304             if (num_markers == 0)
1305                 num_markers = GETJOCTET(marker->data[13]);
1306             else if (num_markers != GETJOCTET(marker->data[13])) {
1307                 JNU_ThrowByName(env, "javax/imageio/IIOException",
1308                      "Invalid icc profile: inconsistent num_markers fields");
1309                 return NULL;
1310             }
1311             seq_no = GETJOCTET(marker->data[12]);
1312 
1313             /* Some third-party tools produce images with profile chunk
1314              * numeration started from zero. It is inconsistent with ICC
1315              * spec, but seems to be recognized by majority of image
1316              * processing tools, so we should be more tolerant to this
1317              * departure from the spec.
1318              */
1319             if (seq_no < 0 || seq_no > num_markers) {
1320                 JNU_ThrowByName(env, "javax/imageio/IIOException",
1321                      "Invalid icc profile: bad sequence number");
1322                 return NULL;
1323             }
1324             if (icc_markers[seq_no] != NULL) {
1325                 JNU_ThrowByName(env, "javax/imageio/IIOException",
1326                      "Invalid icc profile: duplicate sequence numbers");
1327                 return NULL;
1328             }
1329             icc_markers[seq_no] = marker;
1330             num_found_markers ++;
1331         }
1332     }
1333 
1334     if (num_markers == 0)
1335         return NULL;  // There is no profile
1336 
1337     if (num_markers != num_found_markers) {
1338         JNU_ThrowByName(env, "javax/imageio/IIOException",
1339                         "Invalid icc profile: invalid number of icc markers");
1340         return NULL;
1341     }
1342 
1343     first = icc_markers[0] ? 0 : 1;
1344     last = num_found_markers + first;
1345 
1346     /* Check for missing markers, count total space needed.
1347      */
1348     total_length = 0;
1349     for (seq_no = first; seq_no < last; seq_no++) {
1350         unsigned int length;
1351         if (icc_markers[seq_no] == NULL) {
1352             JNU_ThrowByName(env, "javax/imageio/IIOException",
1353                  "Invalid icc profile: missing sequence number");
1354             return NULL;
1355         }
1356         /* check the data length correctness */
1357         length = icc_markers[seq_no]->data_length;
1358         if (ICC_OVERHEAD_LEN > length || length > MAX_BYTES_IN_MARKER) {
1359             JNU_ThrowByName(env, "javax/imageio/IIOException",
1360                  "Invalid icc profile: invalid data length");
1361             return NULL;
1362         }
1363         total_length += (length - ICC_OVERHEAD_LEN);
1364     }
1365 
1366     if (total_length <= 0) {
1367         JNU_ThrowByName(env, "javax/imageio/IIOException",
1368               "Invalid icc profile: found only empty markers");
1369         return NULL;
1370     }
1371 
1372     /* Allocate a Java byte array for assembled data */
1373 
1374     data = (*env)->NewByteArray(env, total_length);
1375     if (data == NULL) {
1376         JNU_ThrowByName(env,
1377                         "java/lang/OutOfMemoryError",
1378                         "Reading ICC profile");
1379         return NULL;
1380     }
1381 
1382     icc_data = (JOCTET *)(*env)->GetPrimitiveArrayCritical(env,
1383                                                            data,
1384                                                            NULL);
1385     if (icc_data == NULL) {
1386         JNU_ThrowByName(env, "javax/imageio/IIOException",
1387                         "Unable to pin icc profile data array");
1388         return NULL;
1389     }
1390 
1391     /* and fill it in */
1392     dst_ptr = icc_data;
1393     for (seq_no = first; seq_no < last; seq_no++) {
1394         JOCTET FAR *src_ptr = icc_markers[seq_no]->data + ICC_OVERHEAD_LEN;
1395         unsigned int length =
1396             icc_markers[seq_no]->data_length - ICC_OVERHEAD_LEN;
1397 
1398         memcpy(dst_ptr, src_ptr, length);
1399         dst_ptr += length;
1400     }
1401 
1402     /* finally, unpin the array */
1403     (*env)->ReleasePrimitiveArrayCritical(env,
1404                                           data,
1405                                           icc_data,
1406                                           0);
1407 
1408 
1409     return data;
1410 }
1411 
1412 /********************* end of ICC profile support *************/
1413 
1414 /********************* Reader JNI calls ***********************/
1415 
1416 JNIEXPORT void JNICALL
1417 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_initReaderIDs
1418     (JNIEnv *env,
1419      jclass cls,
1420      jclass ImageInputStreamClass,
1421      jclass qTableClass,
1422      jclass huffClass) {
1423 
1424     CHECK_NULL(JPEGImageReader_readInputDataID = (*env)->GetMethodID(env,
1425                                                   cls,
1426                                                   "readInputData",
1427                                                   "([BII)I"));
1428     CHECK_NULL(JPEGImageReader_skipInputBytesID = (*env)->GetMethodID(env,
1429                                                        cls,
1430                                                        "skipInputBytes",
1431                                                        "(J)J"));
1432     CHECK_NULL(JPEGImageReader_warningOccurredID = (*env)->GetMethodID(env,
1433                                                             cls,
1434                                                             "warningOccurred",
1435                                                             "(I)V"));
1436     CHECK_NULL(JPEGImageReader_warningWithMessageID =
1437         (*env)->GetMethodID(env,
1438                             cls,
1439                             "warningWithMessage",
1440                             "(Ljava/lang/String;)V"));
1441     CHECK_NULL(JPEGImageReader_setImageDataID = (*env)->GetMethodID(env,
1442                                                          cls,
1443                                                          "setImageData",
1444                                                          "(IIIII[B)V"));
1445     CHECK_NULL(JPEGImageReader_acceptPixelsID = (*env)->GetMethodID(env,
1446                                                          cls,
1447                                                          "acceptPixels",
1448                                                          "(IZ)V"));
1449     CHECK_NULL(JPEGImageReader_passStartedID = (*env)->GetMethodID(env,
1450                                                         cls,
1451                                                         "passStarted",
1452                                                         "(I)V"));
1453     CHECK_NULL(JPEGImageReader_passCompleteID = (*env)->GetMethodID(env,
1454                                                          cls,
1455                                                          "passComplete",
1456                                                          "()V"));
1457     CHECK_NULL(JPEGImageReader_pushBackID = (*env)->GetMethodID(env,
1458                                                      cls,
1459                                                      "pushBack",
1460                                                      "(I)V"));
1461     CHECK_NULL(JPEGQTable_tableID = (*env)->GetFieldID(env,
1462                                             qTableClass,
1463                                             "qTable",
1464                                             "[I"));
1465 
1466     CHECK_NULL(JPEGHuffmanTable_lengthsID = (*env)->GetFieldID(env,
1467                                                     huffClass,
1468                                                     "lengths",
1469                                                     "[S"));
1470 
1471     CHECK_NULL(JPEGHuffmanTable_valuesID = (*env)->GetFieldID(env,
1472                                                     huffClass,
1473                                                     "values",
1474                                                     "[S"));
1475 }
1476 
1477 JNIEXPORT jlong JNICALL
1478 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_initJPEGImageReader
1479     (JNIEnv *env,
1480      jobject this) {
1481 
1482     imageIODataPtr ret;
1483     struct sun_jpeg_error_mgr *jerr;
1484 
1485     /* This struct contains the JPEG decompression parameters and pointers to
1486      * working space (which is allocated as needed by the JPEG library).
1487      */
1488     struct jpeg_decompress_struct *cinfo =
1489         malloc(sizeof(struct jpeg_decompress_struct));
1490     if (cinfo == NULL) {
1491         JNU_ThrowByName( env,
1492                          "java/lang/OutOfMemoryError",
1493                          "Initializing Reader");
1494         return 0;
1495     }
1496 
1497     /* We use our private extension JPEG error handler.
1498      */
1499     jerr = malloc (sizeof(struct sun_jpeg_error_mgr));
1500     if (jerr == NULL) {
1501         JNU_ThrowByName( env,
1502                          "java/lang/OutOfMemoryError",
1503                          "Initializing Reader");
1504         free(cinfo);
1505         return 0;
1506     }
1507 
1508     /* We set up the normal JPEG error routines, then override error_exit. */
1509     cinfo->err = jpeg_std_error(&(jerr->pub));
1510     jerr->pub.error_exit = sun_jpeg_error_exit;
1511     /* We need to setup our own print routines */
1512     jerr->pub.output_message = sun_jpeg_output_message;
1513     /* Now we can setjmp before every call to the library */
1514 
1515     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
1516     if (setjmp(jerr->setjmp_buffer)) {
1517         /* If we get here, the JPEG code has signaled an error. */
1518         char buffer[JMSG_LENGTH_MAX];
1519         (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,
1520                                       buffer);
1521         JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
1522         return 0;
1523     }
1524 
1525     /* Perform library initialization */
1526     jpeg_create_decompress(cinfo);
1527 
1528     // Set up to keep any APP2 markers, as these might contain ICC profile
1529     // data
1530     jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF);
1531 
1532     /*
1533      * Now set up our source.
1534      */
1535     cinfo->src =
1536         (struct jpeg_source_mgr *) malloc (sizeof(struct jpeg_source_mgr));
1537     if (cinfo->src == NULL) {
1538         JNU_ThrowByName(env,
1539                         "java/lang/OutOfMemoryError",
1540                         "Initializing Reader");
1541         imageio_dispose((j_common_ptr)cinfo);
1542         return 0;
1543     }
1544     cinfo->src->bytes_in_buffer = 0;
1545     cinfo->src->next_input_byte = NULL;
1546     cinfo->src->init_source = imageio_init_source;
1547     cinfo->src->fill_input_buffer = imageio_fill_input_buffer;
1548     cinfo->src->skip_input_data = imageio_skip_input_data;
1549     cinfo->src->resync_to_restart = jpeg_resync_to_restart; // use default
1550     cinfo->src->term_source = imageio_term_source;
1551 
1552     /* set up the association to persist for future calls */
1553     ret = initImageioData(env, (j_common_ptr) cinfo, this);
1554     if (ret == NULL) {
1555         (*env)->ExceptionClear(env);
1556         JNU_ThrowByName(env, "java/lang/OutOfMemoryError",
1557                         "Initializing Reader");
1558         imageio_dispose((j_common_ptr)cinfo);
1559         return 0;
1560     }
1561     return ptr_to_jlong(ret);
1562 }
1563 
1564 /*
1565  * When we set a source from Java, we set up the stream in the streamBuf
1566  * object.  If there was an old one, it is released first.
1567  */
1568 
1569 JNIEXPORT void JNICALL
1570 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setSource
1571     (JNIEnv *env,
1572      jobject this,
1573      jlong ptr) {
1574 
1575     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
1576     j_common_ptr cinfo;
1577 
1578     if (data == NULL) {
1579         JNU_ThrowByName(env,
1580                         "java/lang/IllegalStateException",
1581                         "Attempting to use reader after dispose()");
1582         return;
1583     }
1584 
1585     cinfo = data->jpegObj;
1586 
1587     imageio_set_stream(env, cinfo, data, this);
1588 
1589     imageio_init_source((j_decompress_ptr) cinfo);
1590 }
1591 
1592 #define JPEG_APP1  (JPEG_APP0 + 1)  /* EXIF APP1 marker code  */
1593 
1594 /*
1595  * For EXIF images, the APP1 will appear immediately after the SOI,
1596  * so it's safe to only look at the first marker in the list.
1597  * (see http://www.exif.org/Exif2-2.PDF, section 4.7, page 58)
1598  */
1599 #define IS_EXIF(c) \
1600     (((c)->marker_list != NULL) && ((c)->marker_list->marker == JPEG_APP1))
1601 
1602 JNIEXPORT jboolean JNICALL
1603 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader
1604     (JNIEnv *env,
1605      jobject this,
1606      jlong ptr,
1607      jboolean clearFirst,
1608      jboolean reset) {
1609 
1610     int ret;
1611     int h_samp0, h_samp1, h_samp2;
1612     int v_samp0, v_samp1, v_samp2;
1613     int cid0, cid1, cid2;
1614     jboolean retval = JNI_FALSE;
1615     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
1616     j_decompress_ptr cinfo;
1617     struct jpeg_source_mgr *src;
1618     sun_jpeg_error_ptr jerr;
1619     jbyteArray profileData = NULL;
1620 
1621     if (data == NULL) {
1622         JNU_ThrowByName(env,
1623                         "java/lang/IllegalStateException",
1624                         "Attempting to use reader after dispose()");
1625         return JNI_FALSE;
1626     }
1627 
1628     cinfo = (j_decompress_ptr) data->jpegObj;
1629     src = cinfo->src;
1630 
1631     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
1632     jerr = (sun_jpeg_error_ptr) cinfo->err;
1633 
1634     if (setjmp(jerr->setjmp_buffer)) {
1635         /* If we get here, the JPEG code has signaled an error
1636            while reading the header. */
1637         RELEASE_ARRAYS(env, data, src->next_input_byte);
1638         if (!(*env)->ExceptionOccurred(env)) {
1639             char buffer[JMSG_LENGTH_MAX];
1640             (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,
1641                                           buffer);
1642             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
1643         }
1644         return retval;
1645     }
1646 
1647 #ifdef DEBUG_IIO_JPEG
1648     printf("In readImageHeader, data is %p cinfo is %p\n", data, cinfo);
1649     printf("clearFirst is %d\n", clearFirst);
1650 #endif
1651 
1652     if (GET_ARRAYS(env, data, &src->next_input_byte) == NOT_OK) {
1653         (*env)->ExceptionClear(env);
1654         JNU_ThrowByName(env,
1655                         "javax/imageio/IIOException",
1656                         "Array pin failed");
1657         return retval;
1658     }
1659 
1660     /*
1661      * Now clear the input buffer if the Java code has done a seek
1662      * on the stream since the last call, invalidating any buffer contents.
1663      */
1664     if (clearFirst) {
1665         clearStreamBuffer(&data->streamBuf);
1666         src->next_input_byte = NULL;
1667         src->bytes_in_buffer = 0;
1668     }
1669 
1670     ret = jpeg_read_header(cinfo, FALSE);
1671 
1672     if (ret == JPEG_HEADER_TABLES_ONLY) {
1673         retval = JNI_TRUE;
1674         imageio_term_source(cinfo);  // Pushback remaining buffer contents
1675 #ifdef DEBUG_IIO_JPEG
1676         printf("just read tables-only image; q table 0 at %p\n",
1677                cinfo->quant_tbl_ptrs[0]);
1678 #endif
1679         RELEASE_ARRAYS(env, data, src->next_input_byte);
1680     } else {
1681         /*
1682          * Now adjust the jpeg_color_space variable, which was set in
1683          * default_decompress_parms, to reflect our differences from IJG
1684          */
1685 
1686         switch (cinfo->jpeg_color_space) {
1687         default :
1688           break;
1689         case JCS_YCbCr:
1690 
1691             /*
1692              * There are several possibilities:
1693              *  - we got image with embeded colorspace
1694              *     Use it. User knows what he is doing.
1695              *  - we got JFIF image
1696              *     Must be YCbCr (see http://www.w3.org/Graphics/JPEG/jfif3.pdf, page 2)
1697              *  - we got EXIF image
1698              *     Must be YCbCr (see http://www.exif.org/Exif2-2.PDF, section 4.7, page 63)
1699              *  - something else
1700              *     Apply heuristical rules to identify actual colorspace.
1701              */
1702 
1703             if (cinfo->saw_Adobe_marker) {
1704                 if (cinfo->Adobe_transform != 1) {
1705                     /*
1706                      * IJG guesses this is YCbCr and emits a warning
1707                      * We would rather not guess.  Then the user knows
1708                      * To read this as a Raster if at all
1709                      */
1710                     cinfo->jpeg_color_space = JCS_UNKNOWN;
1711                     cinfo->out_color_space = JCS_UNKNOWN;
1712                 }
1713             } else if (!cinfo->saw_JFIF_marker && !IS_EXIF(cinfo)) {
1714                 /*
1715                  * In the absence of certain markers, IJG has interpreted
1716                  * component id's of [1,2,3] as meaning YCbCr.We follow that
1717                  * interpretation, which is additionally described in the Image
1718                  * I/O JPEG metadata spec.If that condition is not met here the
1719                  * next step will be to examine the subsampling factors, if
1720                  * there is any difference in subsampling factors we also assume
1721                  * YCbCr, only if both horizontal and vertical subsampling
1722                  * is same we assume JPEG color space as RGB.
1723                  * This is also described in the Image I/O JPEG metadata spec.
1724                  */
1725                 h_samp0 = cinfo->comp_info[0].h_samp_factor;
1726                 h_samp1 = cinfo->comp_info[1].h_samp_factor;
1727                 h_samp2 = cinfo->comp_info[2].h_samp_factor;
1728 
1729                 v_samp0 = cinfo->comp_info[0].v_samp_factor;
1730                 v_samp1 = cinfo->comp_info[1].v_samp_factor;
1731                 v_samp2 = cinfo->comp_info[2].v_samp_factor;
1732 
1733                 cid0 = cinfo->comp_info[0].component_id;
1734                 cid1 = cinfo->comp_info[1].component_id;
1735                 cid2 = cinfo->comp_info[2].component_id;
1736 
1737                 if ((!(cid0 == 1 && cid1 == 2 && cid2 == 3)) &&
1738                     ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) &&
1739                     (v_samp1 == v_samp0) && (v_samp2 == v_samp0)))
1740                 {
1741                     cinfo->jpeg_color_space = JCS_RGB;
1742                     /* output is already RGB, so it stays the same */
1743                 }
1744             }
1745             break;
1746 #ifdef YCCALPHA
1747         case JCS_YCC:
1748             cinfo->out_color_space = JCS_YCC;
1749             break;
1750 #endif
1751         case JCS_YCCK:
1752             if ((cinfo->saw_Adobe_marker) && (cinfo->Adobe_transform != 2)) {
1753                 /*
1754                  * IJG guesses this is YCCK and emits a warning
1755                  * We would rather not guess.  Then the user knows
1756                  * To read this as a Raster if at all
1757                  */
1758                 cinfo->jpeg_color_space = JCS_UNKNOWN;
1759                 cinfo->out_color_space = JCS_UNKNOWN;
1760             }
1761             break;
1762         case JCS_CMYK:
1763             /*
1764              * IJG assumes all unidentified 4-channels are CMYK.
1765              * We assume that only if the second two channels are
1766              * not subsampled (either horizontally or vertically).
1767              * If they are, we assume YCCK.
1768              */
1769             h_samp0 = cinfo->comp_info[0].h_samp_factor;
1770             h_samp1 = cinfo->comp_info[1].h_samp_factor;
1771             h_samp2 = cinfo->comp_info[2].h_samp_factor;
1772 
1773             v_samp0 = cinfo->comp_info[0].v_samp_factor;
1774             v_samp1 = cinfo->comp_info[1].v_samp_factor;
1775             v_samp2 = cinfo->comp_info[2].v_samp_factor;
1776 
1777             if ((h_samp1 > h_samp0) && (h_samp2 > h_samp0) ||
1778                 (v_samp1 > v_samp0) && (v_samp2 > v_samp0))
1779             {
1780                 cinfo->jpeg_color_space = JCS_YCCK;
1781                 /* Leave the output space as CMYK */
1782             }
1783         }
1784         RELEASE_ARRAYS(env, data, src->next_input_byte);
1785 
1786         /* read icc profile data */
1787         profileData = read_icc_profile(env, cinfo);
1788 
1789         if ((*env)->ExceptionCheck(env)) {
1790             return retval;
1791         }
1792 
1793         (*env)->CallVoidMethod(env, this,
1794                                JPEGImageReader_setImageDataID,
1795                                cinfo->image_width,
1796                                cinfo->image_height,
1797                                cinfo->jpeg_color_space,
1798                                cinfo->out_color_space,
1799                                cinfo->num_components,
1800                                profileData);
1801         if (reset) {
1802             jpeg_abort_decompress(cinfo);
1803         }
1804     }
1805 
1806     return retval;
1807 }
1808 
1809 
1810 JNIEXPORT void JNICALL
1811 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setOutColorSpace
1812     (JNIEnv *env,
1813      jobject this,
1814      jlong ptr,
1815      jint code) {
1816 
1817     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
1818     j_decompress_ptr cinfo;
1819 
1820     if (data == NULL) {
1821         JNU_ThrowByName(env,
1822                         "java/lang/IllegalStateException",
1823                         "Attempting to use reader after dispose()");
1824         return;
1825     }
1826 
1827     cinfo = (j_decompress_ptr) data->jpegObj;
1828 
1829     cinfo->out_color_space = code;
1830 
1831 }
1832 
1833 JNIEXPORT jboolean JNICALL
1834 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
1835     (JNIEnv *env,
1836      jobject this,
1837      jlong ptr,
1838      jbyteArray buffer,
1839      jint numBands,
1840      jintArray srcBands,
1841      jintArray bandSizes,
1842      jint sourceXStart,
1843      jint sourceYStart,
1844      jint sourceWidth,
1845      jint sourceHeight,
1846      jint stepX,
1847      jint stepY,
1848      jobjectArray qtables,
1849      jobjectArray DCHuffmanTables,
1850      jobjectArray ACHuffmanTables,
1851      jint minProgressivePass,  // Counts from 0
1852      jint maxProgressivePass,
1853      jboolean wantUpdates) {
1854 
1855 
1856     struct jpeg_source_mgr *src;
1857     JSAMPROW scanLinePtr = NULL;
1858     jint bands[MAX_BANDS];
1859     int i;
1860     jint *body;
1861     int scanlineLimit;
1862     int pixelStride;
1863     unsigned char *in, *out, *pixelLimit;
1864     int targetLine;
1865     int skipLines, linesLeft;
1866     pixelBufferPtr pb;
1867     sun_jpeg_error_ptr jerr;
1868     boolean done;
1869     boolean mustScale = FALSE;
1870     boolean progressive = FALSE;
1871     boolean orderedBands = TRUE;
1872     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
1873     j_decompress_ptr cinfo;
1874     size_t numBytes;
1875 
1876     /* verify the inputs */
1877 
1878     if (data == NULL) {
1879         JNU_ThrowByName(env,
1880                         "java/lang/IllegalStateException",
1881                         "Attempting to use reader after dispose()");
1882         return JNI_FALSE;
1883     }
1884 
1885     if ((buffer == NULL) || (srcBands == NULL))  {
1886         JNU_ThrowNullPointerException(env, 0);
1887         return JNI_FALSE;
1888     }
1889 
1890     cinfo = (j_decompress_ptr) data->jpegObj;
1891 
1892     if ((numBands < 1) || (numBands > MAX_BANDS) ||
1893         (sourceXStart < 0) || (sourceXStart >= (jint)cinfo->image_width) ||
1894         (sourceYStart < 0) || (sourceYStart >= (jint)cinfo->image_height) ||
1895         (sourceWidth < 1) || (sourceWidth > (jint)cinfo->image_width) ||
1896         (sourceHeight < 1) || (sourceHeight > (jint)cinfo->image_height) ||
1897         (stepX < 1) || (stepY < 1) ||
1898         (minProgressivePass < 0) ||
1899         (maxProgressivePass < minProgressivePass))
1900     {
1901         JNU_ThrowByName(env, "javax/imageio/IIOException",
1902                         "Invalid argument to native readImage");
1903         return JNI_FALSE;
1904     }
1905 
1906     if (stepX > (jint)cinfo->image_width) {
1907         stepX = cinfo->image_width;
1908     }
1909     if (stepY > (jint)cinfo->image_height) {
1910         stepY = cinfo->image_height;
1911     }
1912 
1913     /*
1914      * First get the source bands array and copy it to our local array
1915      * so we don't have to worry about pinning and unpinning it again.
1916      */
1917 
1918     body = (*env)->GetIntArrayElements(env, srcBands, NULL);
1919     if (body == NULL) {
1920         (*env)->ExceptionClear(env);
1921         JNU_ThrowByName( env,
1922                          "java/lang/OutOfMemoryError",
1923                          "Initializing Read");
1924         return JNI_FALSE;
1925     }
1926 
1927     for (i = 0; i < numBands; i++) {
1928         bands[i] = body[i];
1929         if (orderedBands && (bands[i] != i)) {
1930             orderedBands = FALSE;
1931         }
1932     }
1933 
1934     (*env)->ReleaseIntArrayElements(env, srcBands, body, JNI_ABORT);
1935 
1936 #ifdef DEBUG_IIO_JPEG
1937     printf("---- in reader.read ----\n");
1938     printf("numBands is %d\n", numBands);
1939     printf("bands array: ");
1940     for (i = 0; i < numBands; i++) {
1941         printf("%d ", bands[i]);
1942     }
1943     printf("\n");
1944     printf("jq table 0 at %p\n",
1945                cinfo->quant_tbl_ptrs[0]);
1946 #endif
1947 
1948     data = (imageIODataPtr) cinfo->client_data;
1949     src = cinfo->src;
1950 
1951     /* Set the buffer as our PixelBuffer */
1952     pb = &data->pixelBuf;
1953 
1954     if (setPixelBuffer(env, pb, buffer) == NOT_OK) {
1955         return data->abortFlag;  // We already threw an out of memory exception
1956     }
1957 
1958     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
1959     jerr = (sun_jpeg_error_ptr) cinfo->err;
1960 
1961     if (setjmp(jerr->setjmp_buffer)) {
1962         /* If we get here, the JPEG code has signaled an error
1963            while reading. */
1964         RELEASE_ARRAYS(env, data, src->next_input_byte);
1965         if (!(*env)->ExceptionOccurred(env)) {
1966             char buffer[JMSG_LENGTH_MAX];
1967             (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,
1968                                           buffer);
1969             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
1970         }
1971         if (scanLinePtr != NULL) {
1972             free(scanLinePtr);
1973             scanLinePtr = NULL;
1974         }
1975         return data->abortFlag;
1976     }
1977 
1978     if (GET_ARRAYS(env, data, &src->next_input_byte) == NOT_OK) {
1979         (*env)->ExceptionClear(env);
1980         JNU_ThrowByName(env,
1981                         "javax/imageio/IIOException",
1982                         "Array pin failed");
1983         return data->abortFlag;
1984     }
1985 
1986     // If there are no tables in our structure and table arguments aren't
1987     // NULL, use the table arguments.
1988     if ((qtables != NULL) && (cinfo->quant_tbl_ptrs[0] == NULL)) {
1989         (void) setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);
1990     }
1991 
1992     if ((DCHuffmanTables != NULL) && (cinfo->dc_huff_tbl_ptrs[0] == NULL)) {
1993         setHTables(env, (j_common_ptr) cinfo,
1994                    DCHuffmanTables,
1995                    ACHuffmanTables,
1996                    TRUE);
1997     }
1998 
1999     progressive = jpeg_has_multiple_scans(cinfo);
2000     if (progressive) {
2001         cinfo->buffered_image = TRUE;
2002         cinfo->input_scan_number = minProgressivePass+1; // Java count from 0
2003 #define MAX_JAVA_INT 2147483647 // XXX Is this defined in JNI somewhere?
2004         if (maxProgressivePass < MAX_JAVA_INT) {
2005             maxProgressivePass++; // For testing
2006         }
2007     }
2008 
2009     data->streamBuf.suspendable = FALSE;
2010 
2011     jpeg_start_decompress(cinfo);
2012 
2013     if (numBands !=  cinfo->output_components) {
2014         JNU_ThrowByName(env, "javax/imageio/IIOException",
2015                         "Invalid argument to native readImage");
2016         return data->abortFlag;
2017     }
2018 
2019     if (cinfo->output_components <= 0 ||
2020         cinfo->image_width > (0xffffffffu / (unsigned int)cinfo->output_components))
2021     {
2022         JNU_ThrowByName(env, "javax/imageio/IIOException",
2023                         "Invalid number of output components");
2024         return data->abortFlag;
2025     }
2026 
2027     // Allocate a 1-scanline buffer
2028     scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->output_components);
2029     if (scanLinePtr == NULL) {
2030         RELEASE_ARRAYS(env, data, src->next_input_byte);
2031         JNU_ThrowByName( env,
2032                          "java/lang/OutOfMemoryError",
2033                          "Reading JPEG Stream");
2034         return data->abortFlag;
2035     }
2036 
2037     // loop over progressive passes
2038     done = FALSE;
2039     while (!done) {
2040         if (progressive) {
2041             // initialize the next pass.  Note that this skips up to
2042             // the first interesting pass.
2043             jpeg_start_output(cinfo, cinfo->input_scan_number);
2044             if (wantUpdates) {
2045                 (*env)->CallVoidMethod(env, this,
2046                                        JPEGImageReader_passStartedID,
2047                                        cinfo->input_scan_number-1);
2048             }
2049         } else if (wantUpdates) {
2050             (*env)->CallVoidMethod(env, this,
2051                                    JPEGImageReader_passStartedID,
2052                                    0);
2053 
2054         }
2055 
2056         // Skip until the first interesting line
2057         while ((data->abortFlag == JNI_FALSE)
2058                && ((jint)cinfo->output_scanline < sourceYStart)) {
2059             jpeg_read_scanlines(cinfo, &scanLinePtr, 1);
2060         }
2061 
2062         scanlineLimit = sourceYStart+sourceHeight;
2063         pixelLimit = scanLinePtr
2064             +(sourceXStart+sourceWidth)*cinfo->output_components;
2065 
2066         pixelStride = stepX*cinfo->output_components;
2067         targetLine = 0;
2068 
2069         while ((data->abortFlag == JNI_FALSE)
2070                && ((jint)cinfo->output_scanline < scanlineLimit)) {
2071 
2072             jpeg_read_scanlines(cinfo, &scanLinePtr, 1);
2073 
2074             // Now mangle it into our buffer
2075             out = data->pixelBuf.buf.bp;
2076 
2077             if (orderedBands && (pixelStride == numBands)) {
2078                 // Optimization: The component bands are ordered sequentially,
2079                 // so we can simply use memcpy() to copy the intermediate
2080                 // scanline buffer into the raster.
2081                 in = scanLinePtr + (sourceXStart * cinfo->output_components);
2082                 if (pixelLimit > in) {
2083                     numBytes = pixelLimit - in;
2084                     if (numBytes > data->pixelBuf.byteBufferLength) {
2085                         numBytes = data->pixelBuf.byteBufferLength;
2086                     }
2087                     memcpy(out, in, numBytes);
2088                 }
2089             } else {
2090                 numBytes = numBands;
2091                 for (in = scanLinePtr+sourceXStart*cinfo->output_components;
2092                      in < pixelLimit &&
2093                        numBytes <= data->pixelBuf.byteBufferLength;
2094                      in += pixelStride) {
2095                     for (i = 0; i < numBands; i++) {
2096                         *out++ = *(in+bands[i]);
2097                     }
2098                     numBytes += numBands;
2099                 }
2100             }
2101 
2102             // And call it back to Java
2103             RELEASE_ARRAYS(env, data, src->next_input_byte);
2104             (*env)->CallVoidMethod(env,
2105                                    this,
2106                                    JPEGImageReader_acceptPixelsID,
2107                                    targetLine++,
2108                                    progressive);
2109 
2110             if ((*env)->ExceptionOccurred(env)
2111                 || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
2112                 cinfo->err->error_exit((j_common_ptr) cinfo);
2113             }
2114 
2115             // And skip over uninteresting lines to the next subsampled line
2116             // Ensure we don't go past the end of the image
2117 
2118             // Lines to skip based on subsampling
2119             skipLines = stepY - 1;
2120             // Lines left in the image
2121             linesLeft =  scanlineLimit - cinfo->output_scanline;
2122             // Take the minimum
2123             if (skipLines > linesLeft) {
2124                 skipLines = linesLeft;
2125             }
2126             for(i = 0; i < skipLines; i++) {
2127                 jpeg_read_scanlines(cinfo, &scanLinePtr, 1);
2128             }
2129         }
2130         if (progressive) {
2131             jpeg_finish_output(cinfo); // Increments pass counter
2132             // Call Java to notify pass complete
2133             if (jpeg_input_complete(cinfo)
2134                 || (cinfo->input_scan_number > maxProgressivePass)) {
2135                 done = TRUE;
2136             }
2137         } else {
2138             done = TRUE;
2139         }
2140         if (wantUpdates) {
2141             (*env)->CallVoidMethod(env, this,
2142                                    JPEGImageReader_passCompleteID);
2143         }
2144 
2145     }
2146     /*
2147      * We are done, but we might not have read all the lines, or all
2148      * the passes, so use jpeg_abort instead of jpeg_finish_decompress.
2149      */
2150     if (cinfo->output_scanline == cinfo->output_height) {
2151         //    if ((cinfo->output_scanline == cinfo->output_height) &&
2152         //(jpeg_input_complete(cinfo))) {  // We read the whole file
2153         jpeg_finish_decompress(cinfo);
2154     } else {
2155         jpeg_abort_decompress(cinfo);
2156     }
2157 
2158     free(scanLinePtr);
2159 
2160     RELEASE_ARRAYS(env, data, src->next_input_byte);
2161 
2162     return data->abortFlag;
2163 }
2164 
2165 JNIEXPORT void JNICALL
2166 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_abortRead
2167     (JNIEnv *env,
2168      jobject this,
2169      jlong ptr) {
2170 
2171     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2172 
2173     if (data == NULL) {
2174         JNU_ThrowByName(env,
2175                         "java/lang/IllegalStateException",
2176                         "Attempting to use reader after dispose()");
2177         return;
2178     }
2179 
2180     imageio_abort(env, this, data);
2181 
2182 }
2183 
2184 JNIEXPORT void JNICALL
2185 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetLibraryState
2186     (JNIEnv *env,
2187      jobject this,
2188      jlong ptr) {
2189     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2190     j_decompress_ptr cinfo;
2191 
2192     if (data == NULL) {
2193         JNU_ThrowByName(env,
2194                         "java/lang/IllegalStateException",
2195                         "Attempting to use reader after dispose()");
2196         return;
2197     }
2198 
2199     cinfo = (j_decompress_ptr) data->jpegObj;
2200 
2201     jpeg_abort_decompress(cinfo);
2202 }
2203 
2204 
2205 JNIEXPORT void JNICALL
2206 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetReader
2207     (JNIEnv *env,
2208      jobject this,
2209      jlong ptr) {
2210 
2211     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2212     j_decompress_ptr cinfo;
2213     sun_jpeg_error_ptr jerr;
2214 
2215     if (data == NULL) {
2216         JNU_ThrowByName(env,
2217                         "java/lang/IllegalStateException",
2218                         "Attempting to use reader after dispose()");
2219         return;
2220     }
2221 
2222     cinfo = (j_decompress_ptr) data->jpegObj;
2223 
2224     jerr = (sun_jpeg_error_ptr) cinfo->err;
2225 
2226     imageio_reset(env, (j_common_ptr) cinfo, data);
2227 
2228     /*
2229      * The tables have not been reset, and there is no way to do so
2230      * in IJG without leaking memory.  The only situation in which
2231      * this will cause a problem is if an image-only stream is read
2232      * with this object without initializing the correct tables first.
2233      * This situation, which should cause an error, might work but
2234      * produce garbage instead.  If the huffman tables are wrong,
2235      * it will fail during the decode.  If the q tables are wrong, it
2236      * will look strange.  This is very unlikely, so don't worry about
2237      * it.  To be really robust, we would keep a flag for table state
2238      * and consult it to catch exceptional situations.
2239      */
2240 
2241     /* above does not clean up the source, so we have to */
2242 
2243     /*
2244       We need to explicitly initialize exception handler or we may
2245        longjump to random address from the term_source()
2246      */
2247 
2248     if (setjmp(jerr->setjmp_buffer)) {
2249 
2250         /*
2251           We may get IOException from pushBack() here.
2252 
2253           However it could be legal if original input stream was closed
2254           earlier (for example because network connection was closed).
2255           Unfortunately, image inputstream API has no way to check whether
2256           stream is already closed or IOException was thrown because of some
2257           other IO problem,
2258           And we can not avoid call to pushBack() on closed stream for the
2259           same reason.
2260 
2261           So, for now we will silently eat this exception.
2262 
2263           NB: this may be changed in future when ImageInputStream API will
2264           become more flexible.
2265         */
2266 
2267         if ((*env)->ExceptionOccurred(env)) {
2268             (*env)->ExceptionClear(env);
2269         }
2270     } else {
2271         cinfo->src->term_source(cinfo);
2272     }
2273 
2274     cinfo->src->bytes_in_buffer = 0;
2275     cinfo->src->next_input_byte = NULL;
2276 }
2277 
2278 JNIEXPORT void JNICALL
2279 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_disposeReader
2280     (JNIEnv *env,
2281      jclass reader,
2282      jlong ptr) {
2283 
2284     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2285     j_common_ptr info = destroyImageioData(env, data);
2286 
2287     imageio_dispose(info);
2288 }
2289 
2290 /********************** end of Reader *************************/
2291 
2292 /********************** Writer Support ************************/
2293 
2294 /********************** Destination Manager *******************/
2295 
2296 METHODDEF(void)
2297 /*
2298  * Initialize destination --- called by jpeg_start_compress
2299  * before any data is actually written.  The data arrays
2300  * must be pinned before this is called.
2301  */
2302 imageio_init_destination (j_compress_ptr cinfo)
2303 {
2304     struct jpeg_destination_mgr *dest = cinfo->dest;
2305     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
2306     streamBufferPtr sb = &data->streamBuf;
2307     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
2308 
2309     if (sb->buf == NULL) {
2310         // We forgot to pin the array
2311         (*env)->FatalError(env, "Output buffer not pinned!");
2312     }
2313 
2314     dest->next_output_byte = sb->buf;
2315     dest->free_in_buffer = sb->bufferLength;
2316 }
2317 
2318 /*
2319  * Empty the output buffer --- called whenever buffer fills up.
2320  *
2321  * This routine writes the entire output buffer
2322  * (ignoring the current state of next_output_byte & free_in_buffer),
2323  * resets the pointer & count to the start of the buffer, and returns TRUE
2324  * indicating that the buffer has been dumped.
2325  */
2326 
2327 METHODDEF(boolean)
2328 imageio_empty_output_buffer (j_compress_ptr cinfo)
2329 {
2330     struct jpeg_destination_mgr *dest = cinfo->dest;
2331     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
2332     streamBufferPtr sb = &data->streamBuf;
2333     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
2334     jobject output = NULL;
2335 
2336     RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2337 
2338     GET_IO_REF(output);
2339 
2340     (*env)->CallVoidMethod(env,
2341                            output,
2342                            JPEGImageWriter_writeOutputDataID,
2343                            sb->hstreamBuffer,
2344                            0,
2345                            sb->bufferLength);
2346     if ((*env)->ExceptionOccurred(env)
2347         || !GET_ARRAYS(env, data,
2348                        (const JOCTET **)(&dest->next_output_byte))) {
2349             cinfo->err->error_exit((j_common_ptr) cinfo);
2350     }
2351 
2352     dest->next_output_byte = sb->buf;
2353     dest->free_in_buffer = sb->bufferLength;
2354 
2355     return TRUE;
2356 }
2357 
2358 /*
2359  * After all of the data has been encoded there may still be some
2360  * more left over in some of the working buffers.  Now is the
2361  * time to clear them out.
2362  */
2363 METHODDEF(void)
2364 imageio_term_destination (j_compress_ptr cinfo)
2365 {
2366     struct jpeg_destination_mgr *dest = cinfo->dest;
2367     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
2368     streamBufferPtr sb = &data->streamBuf;
2369     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
2370 
2371     /* find out how much needs to be written */
2372     /* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */
2373     jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer);
2374 
2375     if (datacount != 0) {
2376         jobject output = NULL;
2377 
2378         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2379 
2380         GET_IO_REF(output);
2381 
2382         (*env)->CallVoidMethod(env,
2383                                output,
2384                                JPEGImageWriter_writeOutputDataID,
2385                                sb->hstreamBuffer,
2386                                0,
2387                                datacount);
2388 
2389         if ((*env)->ExceptionOccurred(env)
2390             || !GET_ARRAYS(env, data,
2391                            (const JOCTET **)(&dest->next_output_byte))) {
2392             cinfo->err->error_exit((j_common_ptr) cinfo);
2393         }
2394     }
2395 
2396     dest->next_output_byte = NULL;
2397     dest->free_in_buffer = 0;
2398 
2399 }
2400 
2401 /*
2402  * Flush the destination buffer.  This is not called by the library,
2403  * but by our code below.  This is the simplest implementation, though
2404  * certainly not the most efficient.
2405  */
2406 METHODDEF(void)
2407 imageio_flush_destination(j_compress_ptr cinfo)
2408 {
2409     imageio_term_destination(cinfo);
2410     imageio_init_destination(cinfo);
2411 }
2412 
2413 /********************** end of destination manager ************/
2414 
2415 /********************** Writer JNI calls **********************/
2416 
2417 
2418 JNIEXPORT void JNICALL
2419 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initWriterIDs
2420     (JNIEnv *env,
2421      jclass cls,
2422      jclass qTableClass,
2423      jclass huffClass) {
2424 
2425     CHECK_NULL(JPEGImageWriter_writeOutputDataID = (*env)->GetMethodID(env,
2426                                                     cls,
2427                                                     "writeOutputData",
2428                                                     "([BII)V"));
2429     CHECK_NULL(JPEGImageWriter_warningOccurredID = (*env)->GetMethodID(env,
2430                                                             cls,
2431                                                             "warningOccurred",
2432                                                             "(I)V"));
2433     CHECK_NULL(JPEGImageWriter_warningWithMessageID =
2434                                         (*env)->GetMethodID(env,
2435                                                             cls,
2436                                                             "warningWithMessage",
2437                                                             "(Ljava/lang/String;)V"));
2438     CHECK_NULL(JPEGImageWriter_writeMetadataID = (*env)->GetMethodID(env,
2439                                                           cls,
2440                                                           "writeMetadata",
2441                                                           "()V"));
2442     CHECK_NULL(JPEGImageWriter_grabPixelsID = (*env)->GetMethodID(env,
2443                                                        cls,
2444                                                        "grabPixels",
2445                                                        "(I)V"));
2446     CHECK_NULL(JPEGQTable_tableID = (*env)->GetFieldID(env,
2447                                             qTableClass,
2448                                             "qTable",
2449                                             "[I"));
2450     CHECK_NULL(JPEGHuffmanTable_lengthsID = (*env)->GetFieldID(env,
2451                                                     huffClass,
2452                                                     "lengths",
2453                                                     "[S"));
2454     CHECK_NULL(JPEGHuffmanTable_valuesID = (*env)->GetFieldID(env,
2455                                                     huffClass,
2456                                                     "values",
2457                                                     "[S"));
2458 }
2459 
2460 JNIEXPORT jlong JNICALL
2461 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initJPEGImageWriter
2462     (JNIEnv *env,
2463      jobject this) {
2464 
2465     imageIODataPtr ret;
2466     struct sun_jpeg_error_mgr *jerr;
2467     struct jpeg_destination_mgr *dest;
2468 
2469     /* This struct contains the JPEG compression parameters and pointers to
2470      * working space (which is allocated as needed by the JPEG library).
2471      */
2472     struct jpeg_compress_struct *cinfo =
2473         malloc(sizeof(struct jpeg_compress_struct));
2474     if (cinfo == NULL) {
2475         JNU_ThrowByName( env,
2476                          "java/lang/OutOfMemoryError",
2477                          "Initializing Writer");
2478         return 0;
2479     }
2480 
2481     /* We use our private extension JPEG error handler.
2482      */
2483     jerr = malloc (sizeof(struct sun_jpeg_error_mgr));
2484     if (jerr == NULL) {
2485         JNU_ThrowByName( env,
2486                          "java/lang/OutOfMemoryError",
2487                          "Initializing Writer");
2488         free(cinfo);
2489         return 0;
2490     }
2491 
2492     /* We set up the normal JPEG error routines, then override error_exit. */
2493     cinfo->err = jpeg_std_error(&(jerr->pub));
2494     jerr->pub.error_exit = sun_jpeg_error_exit;
2495     /* We need to setup our own print routines */
2496     jerr->pub.output_message = sun_jpeg_output_message;
2497     /* Now we can setjmp before every call to the library */
2498 
2499     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
2500     if (setjmp(jerr->setjmp_buffer)) {
2501         /* If we get here, the JPEG code has signaled an error. */
2502         char buffer[JMSG_LENGTH_MAX];
2503         (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,
2504                                       buffer);
2505         JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
2506         return 0;
2507     }
2508 
2509     /* Perform library initialization */
2510     jpeg_create_compress(cinfo);
2511 
2512     /* Now set up the destination  */
2513     dest = malloc(sizeof(struct jpeg_destination_mgr));
2514     if (dest == NULL) {
2515         JNU_ThrowByName( env,
2516                          "java/lang/OutOfMemoryError",
2517                          "Initializing Writer");
2518         imageio_dispose((j_common_ptr)cinfo);
2519         return 0;
2520     }
2521 
2522     dest->init_destination = imageio_init_destination;
2523     dest->empty_output_buffer = imageio_empty_output_buffer;
2524     dest->term_destination = imageio_term_destination;
2525     dest->next_output_byte = NULL;
2526     dest->free_in_buffer = 0;
2527 
2528     cinfo->dest = dest;
2529 
2530     /* set up the association to persist for future calls */
2531     ret = initImageioData(env, (j_common_ptr) cinfo, this);
2532     if (ret == NULL) {
2533         (*env)->ExceptionClear(env);
2534         JNU_ThrowByName( env,
2535                          "java/lang/OutOfMemoryError",
2536                          "Initializing Writer");
2537         imageio_dispose((j_common_ptr)cinfo);
2538         return 0;
2539     }
2540     return ptr_to_jlong(ret);
2541 }
2542 
2543 JNIEXPORT void JNICALL
2544 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_setDest
2545     (JNIEnv *env,
2546      jobject this,
2547      jlong ptr) {
2548 
2549     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2550     j_compress_ptr cinfo;
2551 
2552     if (data == NULL) {
2553         JNU_ThrowByName(env,
2554                         "java/lang/IllegalStateException",
2555                         "Attempting to use writer after dispose()");
2556         return;
2557     }
2558 
2559     cinfo = (j_compress_ptr) data->jpegObj;
2560 
2561     imageio_set_stream(env, data->jpegObj, data, this);
2562 
2563 
2564     // Don't call the init method, as that depends on pinned arrays
2565     cinfo->dest->next_output_byte = NULL;
2566     cinfo->dest->free_in_buffer = 0;
2567 }
2568 
2569 JNIEXPORT void JNICALL
2570 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables
2571     (JNIEnv *env,
2572      jobject this,
2573      jlong ptr,
2574      jobjectArray qtables,
2575      jobjectArray DCHuffmanTables,
2576      jobjectArray ACHuffmanTables) {
2577 
2578     struct jpeg_destination_mgr *dest;
2579     sun_jpeg_error_ptr jerr;
2580     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2581     j_compress_ptr cinfo;
2582 
2583     if (data == NULL) {
2584         JNU_ThrowByName(env,
2585                         "java/lang/IllegalStateException",
2586                         "Attempting to use writer after dispose()");
2587         return;
2588     }
2589 
2590     cinfo = (j_compress_ptr) data->jpegObj;
2591     dest = cinfo->dest;
2592 
2593     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
2594     jerr = (sun_jpeg_error_ptr) cinfo->err;
2595 
2596     if (setjmp(jerr->setjmp_buffer)) {
2597         /* If we get here, the JPEG code has signaled an error
2598            while writing. */
2599         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2600         if (!(*env)->ExceptionOccurred(env)) {
2601             char buffer[JMSG_LENGTH_MAX];
2602             (*cinfo->err->format_message) ((j_common_ptr) cinfo,
2603                                           buffer);
2604             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
2605         }
2606         return;
2607     }
2608 
2609     if (GET_ARRAYS(env, data,
2610                    (const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {
2611         (*env)->ExceptionClear(env);
2612         JNU_ThrowByName(env,
2613                         "javax/imageio/IIOException",
2614                         "Array pin failed");
2615         return;
2616     }
2617 
2618     jpeg_suppress_tables(cinfo, TRUE);  // Suppress writing of any current
2619 
2620     data->streamBuf.suspendable = FALSE;
2621     if (qtables != NULL) {
2622 #ifdef DEBUG_IIO_JPEG
2623         printf("in writeTables: qtables not NULL\n");
2624 #endif
2625         setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);
2626     }
2627 
2628     if (DCHuffmanTables != NULL) {
2629         setHTables(env, (j_common_ptr) cinfo,
2630                    DCHuffmanTables, ACHuffmanTables, TRUE);
2631     }
2632 
2633     jpeg_write_tables(cinfo); // Flushes the buffer for you
2634     RELEASE_ARRAYS(env, data, NULL);
2635 }
2636 
2637 static void freeArray(void** arr, jint size) {
2638     int i;
2639     if (arr != NULL) {
2640         for (i = 0; i < size; i++) {
2641             if (arr[i] != NULL) {
2642                 free(arr[i]);
2643             }
2644         }
2645         free(arr);
2646     }
2647 }
2648 
2649 JNIEXPORT jboolean JNICALL
2650 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage
2651     (JNIEnv *env,
2652      jobject this,
2653      jlong ptr,
2654      jbyteArray buffer,
2655      jint inCs, jint outCs,
2656      jint numBands,
2657      jintArray bandSizes,
2658      jint srcWidth,
2659      jint destWidth, jint destHeight,
2660      jint stepX, jint stepY,
2661      jobjectArray qtables,
2662      jboolean writeDQT,
2663      jobjectArray DCHuffmanTables,
2664      jobjectArray ACHuffmanTables,
2665      jboolean writeDHT,
2666      jboolean optimize,
2667      jboolean progressive,
2668      jint numScans,
2669      jintArray scanInfo,
2670      jintArray componentIds,
2671      jintArray HsamplingFactors,
2672      jintArray VsamplingFactors,
2673      jintArray QtableSelectors,
2674      jboolean haveMetadata,
2675      jint restartInterval) {
2676 
2677     struct jpeg_destination_mgr *dest;
2678     JSAMPROW scanLinePtr;
2679     int i, j;
2680     int pixelStride;
2681     unsigned char *in, *out, *pixelLimit, *scanLineLimit;
2682     unsigned int scanLineSize, pixelBufferSize;
2683     int targetLine;
2684     pixelBufferPtr pb;
2685     sun_jpeg_error_ptr jerr;
2686     jint *ids, *hfactors, *vfactors, *qsels;
2687     jsize qlen, hlen;
2688     int *scanptr;
2689     jint *scanData;
2690     jint *bandSize;
2691     int maxBandValue, halfMaxBandValue;
2692     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2693     j_compress_ptr cinfo;
2694     UINT8** scale = NULL;
2695     boolean success = TRUE;
2696 
2697 
2698     /* verify the inputs */
2699 
2700     if (data == NULL) {
2701         JNU_ThrowByName(env,
2702                         "java/lang/IllegalStateException",
2703                         "Attempting to use writer after dispose()");
2704         return JNI_FALSE;
2705     }
2706 
2707     if ((buffer == NULL) ||
2708         (qtables == NULL) ||
2709         // H tables can be null if optimizing
2710         (componentIds == NULL) ||
2711         (HsamplingFactors == NULL) || (VsamplingFactors == NULL) ||
2712         (QtableSelectors == NULL) ||
2713         ((numScans != 0) && (scanInfo != NULL))) {
2714 
2715         JNU_ThrowNullPointerException(env, 0);
2716         return JNI_FALSE;
2717 
2718     }
2719 
2720     scanLineSize = destWidth * numBands;
2721     if ((inCs < 0) || (inCs > JCS_YCCK) ||
2722         (outCs < 0) || (outCs > JCS_YCCK) ||
2723         (numBands < 1) || (numBands > MAX_BANDS) ||
2724         (srcWidth < 0) ||
2725         (destWidth < 0) || (destWidth > srcWidth) ||
2726         (destHeight < 0) ||
2727         (stepX < 0) || (stepY < 0) ||
2728         ((INT_MAX / numBands) < destWidth))  /* destWidth causes an integer overflow */
2729     {
2730         JNU_ThrowByName(env, "javax/imageio/IIOException",
2731                         "Invalid argument to native writeImage");
2732         return JNI_FALSE;
2733     }
2734 
2735     if (stepX > srcWidth) {
2736         stepX = srcWidth;
2737     }
2738 
2739     bandSize = (*env)->GetIntArrayElements(env, bandSizes, NULL);
2740     CHECK_NULL_RETURN(bandSize, JNI_FALSE);
2741 
2742     for (i = 0; i < numBands; i++) {
2743         if (bandSize[i] <= 0 || bandSize[i] > JPEG_BAND_SIZE) {
2744             (*env)->ReleaseIntArrayElements(env, bandSizes,
2745                                             bandSize, JNI_ABORT);
2746             JNU_ThrowByName(env, "javax/imageio/IIOException", "Invalid Image");
2747             return JNI_FALSE;
2748         }
2749     }
2750 
2751     for (i = 0; i < numBands; i++) {
2752         if (bandSize[i] != JPEG_BAND_SIZE) {
2753             if (scale == NULL) {
2754                 scale = (UINT8**) calloc(numBands, sizeof(UINT8*));
2755 
2756                 if (scale == NULL) {
2757                     (*env)->ReleaseIntArrayElements(env, bandSizes,
2758                                                     bandSize, JNI_ABORT);
2759                     JNU_ThrowByName( env, "java/lang/OutOfMemoryError",
2760                                      "Writing JPEG Stream");
2761                     return JNI_FALSE;
2762                 }
2763             }
2764 
2765             maxBandValue = (1 << bandSize[i]) - 1;
2766 
2767             scale[i] = (UINT8*) malloc((maxBandValue + 1) * sizeof(UINT8));
2768 
2769             if (scale[i] == NULL) {
2770                 // Cleanup before throwing an out of memory exception
2771                 for (j = 0; j < i; j++) {
2772                     free(scale[j]);
2773                 }
2774                 free(scale);
2775                 (*env)->ReleaseIntArrayElements(env, bandSizes,
2776                                                 bandSize, JNI_ABORT);
2777                 JNU_ThrowByName( env, "java/lang/OutOfMemoryError",
2778                                  "Writing JPEG Stream");
2779                 return JNI_FALSE;
2780             }
2781 
2782             halfMaxBandValue = maxBandValue >> 1;
2783 
2784             for (j = 0; j <= maxBandValue; j++) {
2785                 scale[i][j] = (UINT8)
2786                     ((j*MAX_JPEG_BAND_VALUE + halfMaxBandValue)/maxBandValue);
2787             }
2788         }
2789     }
2790 
2791     (*env)->ReleaseIntArrayElements(env, bandSizes,
2792                                     bandSize, JNI_ABORT);
2793 
2794     cinfo = (j_compress_ptr) data->jpegObj;
2795     dest = cinfo->dest;
2796 
2797     /* Set the buffer as our PixelBuffer */
2798     pb = &data->pixelBuf;
2799 
2800     if (setPixelBuffer(env, pb, buffer) == NOT_OK) {
2801         freeArray(scale, numBands);
2802         return data->abortFlag;  // We already threw an out of memory exception
2803     }
2804 
2805     // Allocate a 1-scanline buffer
2806     scanLinePtr = (JSAMPROW)malloc(scanLineSize);
2807     if (scanLinePtr == NULL) {
2808         freeArray(scale, numBands);
2809         JNU_ThrowByName( env,
2810                          "java/lang/OutOfMemoryError",
2811                          "Writing JPEG Stream");
2812         return data->abortFlag;
2813     }
2814     scanLineLimit = scanLinePtr + scanLineSize;
2815 
2816     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
2817     jerr = (sun_jpeg_error_ptr) cinfo->err;
2818 
2819     if (setjmp(jerr->setjmp_buffer)) {
2820         /* If we get here, the JPEG code has signaled an error
2821            while writing. */
2822         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2823         if (!(*env)->ExceptionOccurred(env)) {
2824             char buffer[JMSG_LENGTH_MAX];
2825             (*cinfo->err->format_message) ((j_common_ptr) cinfo,
2826                                           buffer);
2827             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
2828         }
2829 
2830         freeArray(scale, numBands);
2831         free(scanLinePtr);
2832         return data->abortFlag;
2833     }
2834 
2835     // set up parameters
2836     cinfo->image_width = destWidth;
2837     cinfo->image_height = destHeight;
2838     cinfo->input_components = numBands;
2839     cinfo->in_color_space = inCs;
2840 
2841     jpeg_set_defaults(cinfo);
2842 
2843     jpeg_set_colorspace(cinfo, outCs);
2844 
2845     cinfo->optimize_coding = optimize;
2846 
2847     cinfo->write_JFIF_header = FALSE;
2848     cinfo->write_Adobe_marker = FALSE;
2849     // copy componentIds
2850     ids = (*env)->GetIntArrayElements(env, componentIds, NULL);
2851     hfactors = (*env)->GetIntArrayElements(env, HsamplingFactors, NULL);
2852     vfactors = (*env)->GetIntArrayElements(env, VsamplingFactors, NULL);
2853     qsels = (*env)->GetIntArrayElements(env, QtableSelectors, NULL);
2854 
2855     if (ids && hfactors && vfactors && qsels) {
2856         for (i = 0; i < numBands; i++) {
2857             cinfo->comp_info[i].component_id = ids[i];
2858             cinfo->comp_info[i].h_samp_factor = hfactors[i];
2859             cinfo->comp_info[i].v_samp_factor = vfactors[i];
2860             cinfo->comp_info[i].quant_tbl_no = qsels[i];
2861         }
2862     } else {
2863         success = FALSE;
2864     }
2865 
2866     if (ids) {
2867         (*env)->ReleaseIntArrayElements(env, componentIds, ids, JNI_ABORT);
2868     }
2869     if (hfactors) {
2870         (*env)->ReleaseIntArrayElements(env, HsamplingFactors, hfactors, JNI_ABORT);
2871     }
2872     if (vfactors) {
2873         (*env)->ReleaseIntArrayElements(env, VsamplingFactors, vfactors, JNI_ABORT);
2874     }
2875     if (qsels) {
2876         (*env)->ReleaseIntArrayElements(env, QtableSelectors, qsels, JNI_ABORT);
2877     }
2878     if (!success) {
2879         freeArray(scale, numBands);
2880         free(scanLinePtr);
2881         return data->abortFlag;
2882     }
2883 
2884     jpeg_suppress_tables(cinfo, TRUE);  // Disable writing any current
2885 
2886     qlen = setQTables(env, (j_common_ptr) cinfo, qtables, writeDQT);
2887 
2888     if (!optimize) {
2889         // Set the h tables
2890         hlen = setHTables(env,
2891                           (j_common_ptr) cinfo,
2892                           DCHuffmanTables,
2893                           ACHuffmanTables,
2894                           writeDHT);
2895     }
2896 
2897     if (GET_ARRAYS(env, data,
2898                    (const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {
2899         (*env)->ExceptionClear(env);
2900         freeArray(scale, numBands);
2901         free(scanLinePtr);
2902         JNU_ThrowByName(env,
2903                         "javax/imageio/IIOException",
2904                         "Array pin failed");
2905         return data->abortFlag;
2906     }
2907 
2908     data->streamBuf.suspendable = FALSE;
2909 
2910     if (progressive) {
2911         if (numScans == 0) { // then use default scans
2912             jpeg_simple_progression(cinfo);
2913         } else {
2914             cinfo->num_scans = numScans;
2915             // Copy the scanInfo to a local array
2916             // The following is copied from jpeg_simple_progression:
2917   /* Allocate space for script.
2918    * We need to put it in the permanent pool in case the application performs
2919    * multiple compressions without changing the settings.  To avoid a memory
2920    * leak if jpeg_simple_progression is called repeatedly for the same JPEG
2921    * object, we try to re-use previously allocated space, and we allocate
2922    * enough space to handle YCbCr even if initially asked for grayscale.
2923    */
2924             if (cinfo->script_space == NULL
2925                 || cinfo->script_space_size < numScans) {
2926                 cinfo->script_space_size = MAX(numScans, 10);
2927                 cinfo->script_space = (jpeg_scan_info *)
2928                     (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
2929                                                 JPOOL_PERMANENT,
2930                                                 cinfo->script_space_size
2931                                                 * sizeof(jpeg_scan_info));
2932             }
2933             cinfo->scan_info = cinfo->script_space;
2934             scanptr = (int *) cinfo->script_space;
2935             scanData = (*env)->GetIntArrayElements(env, scanInfo, NULL);
2936             if (scanData == NULL) {
2937                 RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2938                 freeArray(scale, numBands);
2939                 free(scanLinePtr);
2940                 return data->abortFlag;
2941             }
2942             // number of jints per scan is 9
2943             // We avoid a memcpy to handle different size ints
2944             for (i = 0; i < numScans*9; i++) {
2945                 scanptr[i] = scanData[i];
2946             }
2947             (*env)->ReleaseIntArrayElements(env, scanInfo,
2948                                             scanData, JNI_ABORT);
2949 
2950         }
2951     }
2952 
2953     cinfo->restart_interval = restartInterval;
2954 
2955 #ifdef DEBUG_IIO_JPEG
2956     printf("writer setup complete, starting compressor\n");
2957 #endif
2958 
2959     // start the compressor; tables must already be set
2960     jpeg_start_compress(cinfo, FALSE); // Leaves sent_table alone
2961 
2962     if (haveMetadata) {
2963         // Flush the buffer
2964         imageio_flush_destination(cinfo);
2965         // Call Java to write the metadata
2966         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2967         (*env)->CallVoidMethod(env,
2968                                this,
2969                                JPEGImageWriter_writeMetadataID);
2970         if ((*env)->ExceptionOccurred(env)
2971             || !GET_ARRAYS(env, data,
2972                            (const JOCTET **)(&dest->next_output_byte))) {
2973                 cinfo->err->error_exit((j_common_ptr) cinfo);
2974          }
2975     }
2976 
2977     targetLine = 0;
2978     pixelBufferSize = srcWidth * numBands;
2979     pixelStride = numBands * stepX;
2980 
2981     // for each line in destHeight
2982     while ((data->abortFlag == JNI_FALSE)
2983            && (cinfo->next_scanline < cinfo->image_height)) {
2984         // get the line from Java
2985         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2986         (*env)->CallVoidMethod(env,
2987                                this,
2988                                JPEGImageWriter_grabPixelsID,
2989                                targetLine);
2990         if ((*env)->ExceptionOccurred(env)
2991             || !GET_ARRAYS(env, data,
2992                            (const JOCTET **)(&dest->next_output_byte))) {
2993                 cinfo->err->error_exit((j_common_ptr) cinfo);
2994          }
2995 
2996         // subsample it into our buffer
2997 
2998         in = data->pixelBuf.buf.bp;
2999         out = scanLinePtr;
3000         pixelLimit = in + ((pixelBufferSize > data->pixelBuf.byteBufferLength) ?
3001                            data->pixelBuf.byteBufferLength : pixelBufferSize);
3002         for (; (in < pixelLimit) && (out < scanLineLimit); in += pixelStride) {
3003             for (i = 0; i < numBands; i++) {
3004                 if (scale !=NULL && scale[i] != NULL) {
3005                     *out++ = scale[i][*(in+i)];
3006 #ifdef DEBUG_IIO_JPEG
3007                     if (in == data->pixelBuf.buf.bp){ // Just the first pixel
3008                         printf("in %d -> out %d, ", *(in+i), *(out-i-1));
3009                     }
3010 #endif
3011 
3012 #ifdef DEBUG_IIO_JPEG
3013                     if (in == data->pixelBuf.buf.bp){ // Just the first pixel
3014                         printf("\n");
3015                     }
3016 #endif
3017                 } else {
3018                     *out++ = *(in+i);
3019                 }
3020             }
3021         }
3022         // write it out
3023         jpeg_write_scanlines(cinfo, (JSAMPARRAY)&scanLinePtr, 1);
3024         targetLine += stepY;
3025     }
3026 
3027     /*
3028      * We are done, but we might not have done all the lines,
3029      * so use jpeg_abort instead of jpeg_finish_compress.
3030      */
3031     if (cinfo->next_scanline == cinfo->image_height) {
3032         jpeg_finish_compress(cinfo);  // Flushes buffer with term_dest
3033     } else {
3034         jpeg_abort((j_common_ptr)cinfo);
3035     }
3036 
3037     freeArray(scale, numBands);
3038     free(scanLinePtr);
3039     RELEASE_ARRAYS(env, data, NULL);
3040     return data->abortFlag;
3041 }
3042 
3043 JNIEXPORT void JNICALL
3044 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_abortWrite
3045     (JNIEnv *env,
3046      jobject this,
3047      jlong ptr) {
3048 
3049     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
3050 
3051     if (data == NULL) {
3052         JNU_ThrowByName(env,
3053                         "java/lang/IllegalStateException",
3054                         "Attempting to use writer after dispose()");
3055         return;
3056     }
3057 
3058     imageio_abort(env, this, data);
3059 }
3060 
3061 JNIEXPORT void JNICALL
3062 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_resetWriter
3063     (JNIEnv *env,
3064      jobject this,
3065      jlong ptr) {
3066     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
3067     j_compress_ptr cinfo;
3068 
3069     if (data == NULL) {
3070         JNU_ThrowByName(env,
3071                         "java/lang/IllegalStateException",
3072                         "Attempting to use writer after dispose()");
3073         return;
3074     }
3075 
3076     cinfo = (j_compress_ptr) data->jpegObj;
3077 
3078     imageio_reset(env, (j_common_ptr) cinfo, data);
3079 
3080     /*
3081      * The tables have not been reset, and there is no way to do so
3082      * in IJG without leaking memory.  The only situation in which
3083      * this will cause a problem is if an image-only stream is written
3084      * with this object without initializing the correct tables first,
3085      * which should not be possible.
3086      */
3087 
3088     cinfo->dest->next_output_byte = NULL;
3089     cinfo->dest->free_in_buffer = 0;
3090 }
3091 
3092 JNIEXPORT void JNICALL
3093 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_disposeWriter
3094     (JNIEnv *env,
3095      jclass writer,
3096      jlong ptr) {
3097 
3098     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
3099     j_common_ptr info = destroyImageioData(env, data);
3100 
3101     imageio_dispose(info);
3102 }