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                  * IJG assumes all unidentified 3-channels are YCbCr.
1716                  * We assume that only if the second two channels are
1717                  * subsampled (either horizontally or vertically).  If not,
1718                  * we assume RGB.
1719                  *
1720                  * 4776576: Some digital cameras output YCbCr JPEG images
1721                  * that do not contain a JFIF APP0 marker but are only
1722                  * vertically subsampled (no horizontal subsampling).
1723                  * We should only assume this is RGB data if the subsampling
1724                  * factors for the second two channels are the same as the
1725                  * first (check both horizontal and vertical factors).
1726                  */
1727                 h_samp0 = cinfo->comp_info[0].h_samp_factor;
1728                 h_samp1 = cinfo->comp_info[1].h_samp_factor;
1729                 h_samp2 = cinfo->comp_info[2].h_samp_factor;
1730 
1731                 v_samp0 = cinfo->comp_info[0].v_samp_factor;
1732                 v_samp1 = cinfo->comp_info[1].v_samp_factor;
1733                 v_samp2 = cinfo->comp_info[2].v_samp_factor;
1734 
1735                 cid0 = cinfo->comp_info[0].component_id;
1736                 cid1 = cinfo->comp_info[1].component_id;
1737                 cid2 = cinfo->comp_info[2].component_id;
1738 
1739                 /*
1740                  * 8041501 : Added extra check to verify component ID's
1741                  * before checking sampling factors, because if we dont
1742                  * check component ID's we will be overwriting decision
1743                  * made in jdapimin.c to set default compression parameters.
1744                  */
1745 
1746                 if ((!(cid0 == 1 && cid1 == 2 && cid2 == 3)) &&
1747                     ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) &&
1748                     (v_samp1 == v_samp0) && (v_samp2 == v_samp0)))
1749                 {
1750                     cinfo->jpeg_color_space = JCS_RGB;
1751                     /* output is already RGB, so it stays the same */
1752                 }
1753             }
1754             break;
1755 #ifdef YCCALPHA
1756         case JCS_YCC:
1757             cinfo->out_color_space = JCS_YCC;
1758             break;
1759 #endif
1760         case JCS_YCCK:
1761             if ((cinfo->saw_Adobe_marker) && (cinfo->Adobe_transform != 2)) {
1762                 /*
1763                  * IJG guesses this is YCCK and emits a warning
1764                  * We would rather not guess.  Then the user knows
1765                  * To read this as a Raster if at all
1766                  */
1767                 cinfo->jpeg_color_space = JCS_UNKNOWN;
1768                 cinfo->out_color_space = JCS_UNKNOWN;
1769             }
1770             break;
1771         case JCS_CMYK:
1772             /*
1773              * IJG assumes all unidentified 4-channels are CMYK.
1774              * We assume that only if the second two channels are
1775              * not subsampled (either horizontally or vertically).
1776              * If they are, we assume YCCK.
1777              */
1778             h_samp0 = cinfo->comp_info[0].h_samp_factor;
1779             h_samp1 = cinfo->comp_info[1].h_samp_factor;
1780             h_samp2 = cinfo->comp_info[2].h_samp_factor;
1781 
1782             v_samp0 = cinfo->comp_info[0].v_samp_factor;
1783             v_samp1 = cinfo->comp_info[1].v_samp_factor;
1784             v_samp2 = cinfo->comp_info[2].v_samp_factor;
1785 
1786             if ((h_samp1 > h_samp0) && (h_samp2 > h_samp0) ||
1787                 (v_samp1 > v_samp0) && (v_samp2 > v_samp0))
1788             {
1789                 cinfo->jpeg_color_space = JCS_YCCK;
1790                 /* Leave the output space as CMYK */
1791             }
1792         }
1793         RELEASE_ARRAYS(env, data, src->next_input_byte);
1794 
1795         /* read icc profile data */
1796         profileData = read_icc_profile(env, cinfo);
1797 
1798         if ((*env)->ExceptionCheck(env)) {
1799             return retval;
1800         }
1801 
1802         (*env)->CallVoidMethod(env, this,
1803                                JPEGImageReader_setImageDataID,
1804                                cinfo->image_width,
1805                                cinfo->image_height,
1806                                cinfo->jpeg_color_space,
1807                                cinfo->out_color_space,
1808                                cinfo->num_components,
1809                                profileData);
1810         if (reset) {
1811             jpeg_abort_decompress(cinfo);
1812         }
1813     }
1814 
1815     return retval;
1816 }
1817 
1818 
1819 JNIEXPORT void JNICALL
1820 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setOutColorSpace
1821     (JNIEnv *env,
1822      jobject this,
1823      jlong ptr,
1824      jint code) {
1825 
1826     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
1827     j_decompress_ptr cinfo;
1828 
1829     if (data == NULL) {
1830         JNU_ThrowByName(env,
1831                         "java/lang/IllegalStateException",
1832                         "Attempting to use reader after dispose()");
1833         return;
1834     }
1835 
1836     cinfo = (j_decompress_ptr) data->jpegObj;
1837 
1838     cinfo->out_color_space = code;
1839 
1840 }
1841 
1842 JNIEXPORT jboolean JNICALL
1843 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImage
1844     (JNIEnv *env,
1845      jobject this,
1846      jlong ptr,
1847      jbyteArray buffer,
1848      jint numBands,
1849      jintArray srcBands,
1850      jintArray bandSizes,
1851      jint sourceXStart,
1852      jint sourceYStart,
1853      jint sourceWidth,
1854      jint sourceHeight,
1855      jint stepX,
1856      jint stepY,
1857      jobjectArray qtables,
1858      jobjectArray DCHuffmanTables,
1859      jobjectArray ACHuffmanTables,
1860      jint minProgressivePass,  // Counts from 0
1861      jint maxProgressivePass,
1862      jboolean wantUpdates) {
1863 
1864 
1865     struct jpeg_source_mgr *src;
1866     JSAMPROW scanLinePtr = NULL;
1867     jint bands[MAX_BANDS];
1868     int i;
1869     jint *body;
1870     int scanlineLimit;
1871     int pixelStride;
1872     unsigned char *in, *out, *pixelLimit;
1873     int targetLine;
1874     int skipLines, linesLeft;
1875     pixelBufferPtr pb;
1876     sun_jpeg_error_ptr jerr;
1877     boolean done;
1878     boolean mustScale = FALSE;
1879     boolean progressive = FALSE;
1880     boolean orderedBands = TRUE;
1881     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
1882     j_decompress_ptr cinfo;
1883     size_t numBytes;
1884 
1885     /* verify the inputs */
1886 
1887     if (data == NULL) {
1888         JNU_ThrowByName(env,
1889                         "java/lang/IllegalStateException",
1890                         "Attempting to use reader after dispose()");
1891         return JNI_FALSE;
1892     }
1893 
1894     if ((buffer == NULL) || (srcBands == NULL))  {
1895         JNU_ThrowNullPointerException(env, 0);
1896         return JNI_FALSE;
1897     }
1898 
1899     cinfo = (j_decompress_ptr) data->jpegObj;
1900 
1901     if ((numBands < 1) || (numBands > MAX_BANDS) ||
1902         (sourceXStart < 0) || (sourceXStart >= (jint)cinfo->image_width) ||
1903         (sourceYStart < 0) || (sourceYStart >= (jint)cinfo->image_height) ||
1904         (sourceWidth < 1) || (sourceWidth > (jint)cinfo->image_width) ||
1905         (sourceHeight < 1) || (sourceHeight > (jint)cinfo->image_height) ||
1906         (stepX < 1) || (stepY < 1) ||
1907         (minProgressivePass < 0) ||
1908         (maxProgressivePass < minProgressivePass))
1909     {
1910         JNU_ThrowByName(env, "javax/imageio/IIOException",
1911                         "Invalid argument to native readImage");
1912         return JNI_FALSE;
1913     }
1914 
1915     if (stepX > (jint)cinfo->image_width) {
1916         stepX = cinfo->image_width;
1917     }
1918     if (stepY > (jint)cinfo->image_height) {
1919         stepY = cinfo->image_height;
1920     }
1921 
1922     /*
1923      * First get the source bands array and copy it to our local array
1924      * so we don't have to worry about pinning and unpinning it again.
1925      */
1926 
1927     body = (*env)->GetIntArrayElements(env, srcBands, NULL);
1928     if (body == NULL) {
1929         (*env)->ExceptionClear(env);
1930         JNU_ThrowByName( env,
1931                          "java/lang/OutOfMemoryError",
1932                          "Initializing Read");
1933         return JNI_FALSE;
1934     }
1935 
1936     for (i = 0; i < numBands; i++) {
1937         bands[i] = body[i];
1938         if (orderedBands && (bands[i] != i)) {
1939             orderedBands = FALSE;
1940         }
1941     }
1942 
1943     (*env)->ReleaseIntArrayElements(env, srcBands, body, JNI_ABORT);
1944 
1945 #ifdef DEBUG_IIO_JPEG
1946     printf("---- in reader.read ----\n");
1947     printf("numBands is %d\n", numBands);
1948     printf("bands array: ");
1949     for (i = 0; i < numBands; i++) {
1950         printf("%d ", bands[i]);
1951     }
1952     printf("\n");
1953     printf("jq table 0 at %p\n",
1954                cinfo->quant_tbl_ptrs[0]);
1955 #endif
1956 
1957     data = (imageIODataPtr) cinfo->client_data;
1958     src = cinfo->src;
1959 
1960     /* Set the buffer as our PixelBuffer */
1961     pb = &data->pixelBuf;
1962 
1963     if (setPixelBuffer(env, pb, buffer) == NOT_OK) {
1964         return data->abortFlag;  // We already threw an out of memory exception
1965     }
1966 
1967     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
1968     jerr = (sun_jpeg_error_ptr) cinfo->err;
1969 
1970     if (setjmp(jerr->setjmp_buffer)) {
1971         /* If we get here, the JPEG code has signaled an error
1972            while reading. */
1973         RELEASE_ARRAYS(env, data, src->next_input_byte);
1974         if (!(*env)->ExceptionOccurred(env)) {
1975             char buffer[JMSG_LENGTH_MAX];
1976             (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,
1977                                           buffer);
1978             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
1979         }
1980         if (scanLinePtr != NULL) {
1981             free(scanLinePtr);
1982             scanLinePtr = NULL;
1983         }
1984         return data->abortFlag;
1985     }
1986 
1987     if (GET_ARRAYS(env, data, &src->next_input_byte) == NOT_OK) {
1988         (*env)->ExceptionClear(env);
1989         JNU_ThrowByName(env,
1990                         "javax/imageio/IIOException",
1991                         "Array pin failed");
1992         return data->abortFlag;
1993     }
1994 
1995     // If there are no tables in our structure and table arguments aren't
1996     // NULL, use the table arguments.
1997     if ((qtables != NULL) && (cinfo->quant_tbl_ptrs[0] == NULL)) {
1998         (void) setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);
1999     }
2000 
2001     if ((DCHuffmanTables != NULL) && (cinfo->dc_huff_tbl_ptrs[0] == NULL)) {
2002         setHTables(env, (j_common_ptr) cinfo,
2003                    DCHuffmanTables,
2004                    ACHuffmanTables,
2005                    TRUE);
2006     }
2007 
2008     progressive = jpeg_has_multiple_scans(cinfo);
2009     if (progressive) {
2010         cinfo->buffered_image = TRUE;
2011         cinfo->input_scan_number = minProgressivePass+1; // Java count from 0
2012 #define MAX_JAVA_INT 2147483647 // XXX Is this defined in JNI somewhere?
2013         if (maxProgressivePass < MAX_JAVA_INT) {
2014             maxProgressivePass++; // For testing
2015         }
2016     }
2017 
2018     data->streamBuf.suspendable = FALSE;
2019 
2020     jpeg_start_decompress(cinfo);
2021 
2022     if (numBands !=  cinfo->output_components) {
2023         JNU_ThrowByName(env, "javax/imageio/IIOException",
2024                         "Invalid argument to native readImage");
2025         return data->abortFlag;
2026     }
2027 
2028     if (cinfo->output_components <= 0 ||
2029         cinfo->image_width > (0xffffffffu / (unsigned int)cinfo->output_components))
2030     {
2031         JNU_ThrowByName(env, "javax/imageio/IIOException",
2032                         "Invalid number of output components");
2033         return data->abortFlag;
2034     }
2035 
2036     // Allocate a 1-scanline buffer
2037     scanLinePtr = (JSAMPROW)malloc(cinfo->image_width*cinfo->output_components);
2038     if (scanLinePtr == NULL) {
2039         RELEASE_ARRAYS(env, data, src->next_input_byte);
2040         JNU_ThrowByName( env,
2041                          "java/lang/OutOfMemoryError",
2042                          "Reading JPEG Stream");
2043         return data->abortFlag;
2044     }
2045 
2046     // loop over progressive passes
2047     done = FALSE;
2048     while (!done) {
2049         if (progressive) {
2050             // initialize the next pass.  Note that this skips up to
2051             // the first interesting pass.
2052             jpeg_start_output(cinfo, cinfo->input_scan_number);
2053             if (wantUpdates) {
2054                 (*env)->CallVoidMethod(env, this,
2055                                        JPEGImageReader_passStartedID,
2056                                        cinfo->input_scan_number-1);
2057             }
2058         } else if (wantUpdates) {
2059             (*env)->CallVoidMethod(env, this,
2060                                    JPEGImageReader_passStartedID,
2061                                    0);
2062 
2063         }
2064 
2065         // Skip until the first interesting line
2066         while ((data->abortFlag == JNI_FALSE)
2067                && ((jint)cinfo->output_scanline < sourceYStart)) {
2068             jpeg_read_scanlines(cinfo, &scanLinePtr, 1);
2069         }
2070 
2071         scanlineLimit = sourceYStart+sourceHeight;
2072         pixelLimit = scanLinePtr
2073             +(sourceXStart+sourceWidth)*cinfo->output_components;
2074 
2075         pixelStride = stepX*cinfo->output_components;
2076         targetLine = 0;
2077 
2078         while ((data->abortFlag == JNI_FALSE)
2079                && ((jint)cinfo->output_scanline < scanlineLimit)) {
2080 
2081             jpeg_read_scanlines(cinfo, &scanLinePtr, 1);
2082 
2083             // Now mangle it into our buffer
2084             out = data->pixelBuf.buf.bp;
2085 
2086             if (orderedBands && (pixelStride == numBands)) {
2087                 // Optimization: The component bands are ordered sequentially,
2088                 // so we can simply use memcpy() to copy the intermediate
2089                 // scanline buffer into the raster.
2090                 in = scanLinePtr + (sourceXStart * cinfo->output_components);
2091                 if (pixelLimit > in) {
2092                     numBytes = pixelLimit - in;
2093                     if (numBytes > data->pixelBuf.byteBufferLength) {
2094                         numBytes = data->pixelBuf.byteBufferLength;
2095                     }
2096                     memcpy(out, in, numBytes);
2097                 }
2098             } else {
2099                 numBytes = numBands;
2100                 for (in = scanLinePtr+sourceXStart*cinfo->output_components;
2101                      in < pixelLimit &&
2102                        numBytes <= data->pixelBuf.byteBufferLength;
2103                      in += pixelStride) {
2104                     for (i = 0; i < numBands; i++) {
2105                         *out++ = *(in+bands[i]);
2106                     }
2107                     numBytes += numBands;
2108                 }
2109             }
2110 
2111             // And call it back to Java
2112             RELEASE_ARRAYS(env, data, src->next_input_byte);
2113             (*env)->CallVoidMethod(env,
2114                                    this,
2115                                    JPEGImageReader_acceptPixelsID,
2116                                    targetLine++,
2117                                    progressive);
2118 
2119             if ((*env)->ExceptionOccurred(env)
2120                 || !GET_ARRAYS(env, data, &(src->next_input_byte))) {
2121                 cinfo->err->error_exit((j_common_ptr) cinfo);
2122             }
2123 
2124             // And skip over uninteresting lines to the next subsampled line
2125             // Ensure we don't go past the end of the image
2126 
2127             // Lines to skip based on subsampling
2128             skipLines = stepY - 1;
2129             // Lines left in the image
2130             linesLeft =  scanlineLimit - cinfo->output_scanline;
2131             // Take the minimum
2132             if (skipLines > linesLeft) {
2133                 skipLines = linesLeft;
2134             }
2135             for(i = 0; i < skipLines; i++) {
2136                 jpeg_read_scanlines(cinfo, &scanLinePtr, 1);
2137             }
2138         }
2139         if (progressive) {
2140             jpeg_finish_output(cinfo); // Increments pass counter
2141             // Call Java to notify pass complete
2142             if (jpeg_input_complete(cinfo)
2143                 || (cinfo->input_scan_number > maxProgressivePass)) {
2144                 done = TRUE;
2145             }
2146         } else {
2147             done = TRUE;
2148         }
2149         if (wantUpdates) {
2150             (*env)->CallVoidMethod(env, this,
2151                                    JPEGImageReader_passCompleteID);
2152         }
2153 
2154     }
2155     /*
2156      * We are done, but we might not have read all the lines, or all
2157      * the passes, so use jpeg_abort instead of jpeg_finish_decompress.
2158      */
2159     if (cinfo->output_scanline == cinfo->output_height) {
2160         //    if ((cinfo->output_scanline == cinfo->output_height) &&
2161         //(jpeg_input_complete(cinfo))) {  // We read the whole file
2162         jpeg_finish_decompress(cinfo);
2163     } else {
2164         jpeg_abort_decompress(cinfo);
2165     }
2166 
2167     free(scanLinePtr);
2168 
2169     RELEASE_ARRAYS(env, data, src->next_input_byte);
2170 
2171     return data->abortFlag;
2172 }
2173 
2174 JNIEXPORT void JNICALL
2175 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_abortRead
2176     (JNIEnv *env,
2177      jobject this,
2178      jlong ptr) {
2179 
2180     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2181 
2182     if (data == NULL) {
2183         JNU_ThrowByName(env,
2184                         "java/lang/IllegalStateException",
2185                         "Attempting to use reader after dispose()");
2186         return;
2187     }
2188 
2189     imageio_abort(env, this, data);
2190 
2191 }
2192 
2193 JNIEXPORT void JNICALL
2194 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetLibraryState
2195     (JNIEnv *env,
2196      jobject this,
2197      jlong ptr) {
2198     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2199     j_decompress_ptr cinfo;
2200 
2201     if (data == NULL) {
2202         JNU_ThrowByName(env,
2203                         "java/lang/IllegalStateException",
2204                         "Attempting to use reader after dispose()");
2205         return;
2206     }
2207 
2208     cinfo = (j_decompress_ptr) data->jpegObj;
2209 
2210     jpeg_abort_decompress(cinfo);
2211 }
2212 
2213 
2214 JNIEXPORT void JNICALL
2215 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_resetReader
2216     (JNIEnv *env,
2217      jobject this,
2218      jlong ptr) {
2219 
2220     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2221     j_decompress_ptr cinfo;
2222     sun_jpeg_error_ptr jerr;
2223 
2224     if (data == NULL) {
2225         JNU_ThrowByName(env,
2226                         "java/lang/IllegalStateException",
2227                         "Attempting to use reader after dispose()");
2228         return;
2229     }
2230 
2231     cinfo = (j_decompress_ptr) data->jpegObj;
2232 
2233     jerr = (sun_jpeg_error_ptr) cinfo->err;
2234 
2235     imageio_reset(env, (j_common_ptr) cinfo, data);
2236 
2237     /*
2238      * The tables have not been reset, and there is no way to do so
2239      * in IJG without leaking memory.  The only situation in which
2240      * this will cause a problem is if an image-only stream is read
2241      * with this object without initializing the correct tables first.
2242      * This situation, which should cause an error, might work but
2243      * produce garbage instead.  If the huffman tables are wrong,
2244      * it will fail during the decode.  If the q tables are wrong, it
2245      * will look strange.  This is very unlikely, so don't worry about
2246      * it.  To be really robust, we would keep a flag for table state
2247      * and consult it to catch exceptional situations.
2248      */
2249 
2250     /* above does not clean up the source, so we have to */
2251 
2252     /*
2253       We need to explicitly initialize exception handler or we may
2254        longjump to random address from the term_source()
2255      */
2256 
2257     if (setjmp(jerr->setjmp_buffer)) {
2258 
2259         /*
2260           We may get IOException from pushBack() here.
2261 
2262           However it could be legal if original input stream was closed
2263           earlier (for example because network connection was closed).
2264           Unfortunately, image inputstream API has no way to check whether
2265           stream is already closed or IOException was thrown because of some
2266           other IO problem,
2267           And we can not avoid call to pushBack() on closed stream for the
2268           same reason.
2269 
2270           So, for now we will silently eat this exception.
2271 
2272           NB: this may be changed in future when ImageInputStream API will
2273           become more flexible.
2274         */
2275 
2276         if ((*env)->ExceptionOccurred(env)) {
2277             (*env)->ExceptionClear(env);
2278         }
2279     } else {
2280         cinfo->src->term_source(cinfo);
2281     }
2282 
2283     cinfo->src->bytes_in_buffer = 0;
2284     cinfo->src->next_input_byte = NULL;
2285 }
2286 
2287 JNIEXPORT void JNICALL
2288 Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_disposeReader
2289     (JNIEnv *env,
2290      jclass reader,
2291      jlong ptr) {
2292 
2293     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2294     j_common_ptr info = destroyImageioData(env, data);
2295 
2296     imageio_dispose(info);
2297 }
2298 
2299 /********************** end of Reader *************************/
2300 
2301 /********************** Writer Support ************************/
2302 
2303 /********************** Destination Manager *******************/
2304 
2305 METHODDEF(void)
2306 /*
2307  * Initialize destination --- called by jpeg_start_compress
2308  * before any data is actually written.  The data arrays
2309  * must be pinned before this is called.
2310  */
2311 imageio_init_destination (j_compress_ptr cinfo)
2312 {
2313     struct jpeg_destination_mgr *dest = cinfo->dest;
2314     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
2315     streamBufferPtr sb = &data->streamBuf;
2316     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
2317 
2318     if (sb->buf == NULL) {
2319         // We forgot to pin the array
2320         (*env)->FatalError(env, "Output buffer not pinned!");
2321     }
2322 
2323     dest->next_output_byte = sb->buf;
2324     dest->free_in_buffer = sb->bufferLength;
2325 }
2326 
2327 /*
2328  * Empty the output buffer --- called whenever buffer fills up.
2329  *
2330  * This routine writes the entire output buffer
2331  * (ignoring the current state of next_output_byte & free_in_buffer),
2332  * resets the pointer & count to the start of the buffer, and returns TRUE
2333  * indicating that the buffer has been dumped.
2334  */
2335 
2336 METHODDEF(boolean)
2337 imageio_empty_output_buffer (j_compress_ptr cinfo)
2338 {
2339     struct jpeg_destination_mgr *dest = cinfo->dest;
2340     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
2341     streamBufferPtr sb = &data->streamBuf;
2342     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
2343     jobject output = NULL;
2344 
2345     RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2346 
2347     GET_IO_REF(output);
2348 
2349     (*env)->CallVoidMethod(env,
2350                            output,
2351                            JPEGImageWriter_writeOutputDataID,
2352                            sb->hstreamBuffer,
2353                            0,
2354                            sb->bufferLength);
2355     if ((*env)->ExceptionOccurred(env)
2356         || !GET_ARRAYS(env, data,
2357                        (const JOCTET **)(&dest->next_output_byte))) {
2358             cinfo->err->error_exit((j_common_ptr) cinfo);
2359     }
2360 
2361     dest->next_output_byte = sb->buf;
2362     dest->free_in_buffer = sb->bufferLength;
2363 
2364     return TRUE;
2365 }
2366 
2367 /*
2368  * After all of the data has been encoded there may still be some
2369  * more left over in some of the working buffers.  Now is the
2370  * time to clear them out.
2371  */
2372 METHODDEF(void)
2373 imageio_term_destination (j_compress_ptr cinfo)
2374 {
2375     struct jpeg_destination_mgr *dest = cinfo->dest;
2376     imageIODataPtr data = (imageIODataPtr) cinfo->client_data;
2377     streamBufferPtr sb = &data->streamBuf;
2378     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
2379 
2380     /* find out how much needs to be written */
2381     /* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */
2382     jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer);
2383 
2384     if (datacount != 0) {
2385         jobject output = NULL;
2386 
2387         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2388 
2389         GET_IO_REF(output);
2390 
2391         (*env)->CallVoidMethod(env,
2392                                output,
2393                                JPEGImageWriter_writeOutputDataID,
2394                                sb->hstreamBuffer,
2395                                0,
2396                                datacount);
2397 
2398         if ((*env)->ExceptionOccurred(env)
2399             || !GET_ARRAYS(env, data,
2400                            (const JOCTET **)(&dest->next_output_byte))) {
2401             cinfo->err->error_exit((j_common_ptr) cinfo);
2402         }
2403     }
2404 
2405     dest->next_output_byte = NULL;
2406     dest->free_in_buffer = 0;
2407 
2408 }
2409 
2410 /*
2411  * Flush the destination buffer.  This is not called by the library,
2412  * but by our code below.  This is the simplest implementation, though
2413  * certainly not the most efficient.
2414  */
2415 METHODDEF(void)
2416 imageio_flush_destination(j_compress_ptr cinfo)
2417 {
2418     imageio_term_destination(cinfo);
2419     imageio_init_destination(cinfo);
2420 }
2421 
2422 /********************** end of destination manager ************/
2423 
2424 /********************** Writer JNI calls **********************/
2425 
2426 
2427 JNIEXPORT void JNICALL
2428 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initWriterIDs
2429     (JNIEnv *env,
2430      jclass cls,
2431      jclass qTableClass,
2432      jclass huffClass) {
2433 
2434     CHECK_NULL(JPEGImageWriter_writeOutputDataID = (*env)->GetMethodID(env,
2435                                                     cls,
2436                                                     "writeOutputData",
2437                                                     "([BII)V"));
2438     CHECK_NULL(JPEGImageWriter_warningOccurredID = (*env)->GetMethodID(env,
2439                                                             cls,
2440                                                             "warningOccurred",
2441                                                             "(I)V"));
2442     CHECK_NULL(JPEGImageWriter_warningWithMessageID =
2443                                         (*env)->GetMethodID(env,
2444                                                             cls,
2445                                                             "warningWithMessage",
2446                                                             "(Ljava/lang/String;)V"));
2447     CHECK_NULL(JPEGImageWriter_writeMetadataID = (*env)->GetMethodID(env,
2448                                                           cls,
2449                                                           "writeMetadata",
2450                                                           "()V"));
2451     CHECK_NULL(JPEGImageWriter_grabPixelsID = (*env)->GetMethodID(env,
2452                                                        cls,
2453                                                        "grabPixels",
2454                                                        "(I)V"));
2455     CHECK_NULL(JPEGQTable_tableID = (*env)->GetFieldID(env,
2456                                             qTableClass,
2457                                             "qTable",
2458                                             "[I"));
2459     CHECK_NULL(JPEGHuffmanTable_lengthsID = (*env)->GetFieldID(env,
2460                                                     huffClass,
2461                                                     "lengths",
2462                                                     "[S"));
2463     CHECK_NULL(JPEGHuffmanTable_valuesID = (*env)->GetFieldID(env,
2464                                                     huffClass,
2465                                                     "values",
2466                                                     "[S"));
2467 }
2468 
2469 JNIEXPORT jlong JNICALL
2470 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initJPEGImageWriter
2471     (JNIEnv *env,
2472      jobject this) {
2473 
2474     imageIODataPtr ret;
2475     struct sun_jpeg_error_mgr *jerr;
2476     struct jpeg_destination_mgr *dest;
2477 
2478     /* This struct contains the JPEG compression parameters and pointers to
2479      * working space (which is allocated as needed by the JPEG library).
2480      */
2481     struct jpeg_compress_struct *cinfo =
2482         malloc(sizeof(struct jpeg_compress_struct));
2483     if (cinfo == NULL) {
2484         JNU_ThrowByName( env,
2485                          "java/lang/OutOfMemoryError",
2486                          "Initializing Writer");
2487         return 0;
2488     }
2489 
2490     /* We use our private extension JPEG error handler.
2491      */
2492     jerr = malloc (sizeof(struct sun_jpeg_error_mgr));
2493     if (jerr == NULL) {
2494         JNU_ThrowByName( env,
2495                          "java/lang/OutOfMemoryError",
2496                          "Initializing Writer");
2497         free(cinfo);
2498         return 0;
2499     }
2500 
2501     /* We set up the normal JPEG error routines, then override error_exit. */
2502     cinfo->err = jpeg_std_error(&(jerr->pub));
2503     jerr->pub.error_exit = sun_jpeg_error_exit;
2504     /* We need to setup our own print routines */
2505     jerr->pub.output_message = sun_jpeg_output_message;
2506     /* Now we can setjmp before every call to the library */
2507 
2508     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
2509     if (setjmp(jerr->setjmp_buffer)) {
2510         /* If we get here, the JPEG code has signaled an error. */
2511         char buffer[JMSG_LENGTH_MAX];
2512         (*cinfo->err->format_message) ((struct jpeg_common_struct *) cinfo,
2513                                       buffer);
2514         JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
2515         return 0;
2516     }
2517 
2518     /* Perform library initialization */
2519     jpeg_create_compress(cinfo);
2520 
2521     /* Now set up the destination  */
2522     dest = malloc(sizeof(struct jpeg_destination_mgr));
2523     if (dest == NULL) {
2524         JNU_ThrowByName( env,
2525                          "java/lang/OutOfMemoryError",
2526                          "Initializing Writer");
2527         imageio_dispose((j_common_ptr)cinfo);
2528         return 0;
2529     }
2530 
2531     dest->init_destination = imageio_init_destination;
2532     dest->empty_output_buffer = imageio_empty_output_buffer;
2533     dest->term_destination = imageio_term_destination;
2534     dest->next_output_byte = NULL;
2535     dest->free_in_buffer = 0;
2536 
2537     cinfo->dest = dest;
2538 
2539     /* set up the association to persist for future calls */
2540     ret = initImageioData(env, (j_common_ptr) cinfo, this);
2541     if (ret == NULL) {
2542         (*env)->ExceptionClear(env);
2543         JNU_ThrowByName( env,
2544                          "java/lang/OutOfMemoryError",
2545                          "Initializing Writer");
2546         imageio_dispose((j_common_ptr)cinfo);
2547         return 0;
2548     }
2549     return ptr_to_jlong(ret);
2550 }
2551 
2552 JNIEXPORT void JNICALL
2553 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_setDest
2554     (JNIEnv *env,
2555      jobject this,
2556      jlong ptr) {
2557 
2558     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2559     j_compress_ptr cinfo;
2560 
2561     if (data == NULL) {
2562         JNU_ThrowByName(env,
2563                         "java/lang/IllegalStateException",
2564                         "Attempting to use writer after dispose()");
2565         return;
2566     }
2567 
2568     cinfo = (j_compress_ptr) data->jpegObj;
2569 
2570     imageio_set_stream(env, data->jpegObj, data, this);
2571 
2572 
2573     // Don't call the init method, as that depends on pinned arrays
2574     cinfo->dest->next_output_byte = NULL;
2575     cinfo->dest->free_in_buffer = 0;
2576 }
2577 
2578 JNIEXPORT void JNICALL
2579 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeTables
2580     (JNIEnv *env,
2581      jobject this,
2582      jlong ptr,
2583      jobjectArray qtables,
2584      jobjectArray DCHuffmanTables,
2585      jobjectArray ACHuffmanTables) {
2586 
2587     struct jpeg_destination_mgr *dest;
2588     sun_jpeg_error_ptr jerr;
2589     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2590     j_compress_ptr cinfo;
2591 
2592     if (data == NULL) {
2593         JNU_ThrowByName(env,
2594                         "java/lang/IllegalStateException",
2595                         "Attempting to use writer after dispose()");
2596         return;
2597     }
2598 
2599     cinfo = (j_compress_ptr) data->jpegObj;
2600     dest = cinfo->dest;
2601 
2602     /* Establish the setjmp return context for sun_jpeg_error_exit to use. */
2603     jerr = (sun_jpeg_error_ptr) cinfo->err;
2604 
2605     if (setjmp(jerr->setjmp_buffer)) {
2606         /* If we get here, the JPEG code has signaled an error
2607            while writing. */
2608         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2609         if (!(*env)->ExceptionOccurred(env)) {
2610             char buffer[JMSG_LENGTH_MAX];
2611             (*cinfo->err->format_message) ((j_common_ptr) cinfo,
2612                                           buffer);
2613             JNU_ThrowByName(env, "javax/imageio/IIOException", buffer);
2614         }
2615         return;
2616     }
2617 
2618     if (GET_ARRAYS(env, data,
2619                    (const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {
2620         (*env)->ExceptionClear(env);
2621         JNU_ThrowByName(env,
2622                         "javax/imageio/IIOException",
2623                         "Array pin failed");
2624         return;
2625     }
2626 
2627     jpeg_suppress_tables(cinfo, TRUE);  // Suppress writing of any current
2628 
2629     data->streamBuf.suspendable = FALSE;
2630     if (qtables != NULL) {
2631 #ifdef DEBUG_IIO_JPEG
2632         printf("in writeTables: qtables not NULL\n");
2633 #endif
2634         setQTables(env, (j_common_ptr) cinfo, qtables, TRUE);
2635     }
2636 
2637     if (DCHuffmanTables != NULL) {
2638         setHTables(env, (j_common_ptr) cinfo,
2639                    DCHuffmanTables, ACHuffmanTables, TRUE);
2640     }
2641 
2642     jpeg_write_tables(cinfo); // Flushes the buffer for you
2643     RELEASE_ARRAYS(env, data, NULL);
2644 }
2645 
2646 JNIEXPORT jboolean JNICALL
2647 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_writeImage
2648     (JNIEnv *env,
2649      jobject this,
2650      jlong ptr,
2651      jbyteArray buffer,
2652      jint inCs, jint outCs,
2653      jint numBands,
2654      jintArray bandSizes,
2655      jint srcWidth,
2656      jint destWidth, jint destHeight,
2657      jint stepX, jint stepY,
2658      jobjectArray qtables,
2659      jboolean writeDQT,
2660      jobjectArray DCHuffmanTables,
2661      jobjectArray ACHuffmanTables,
2662      jboolean writeDHT,
2663      jboolean optimize,
2664      jboolean progressive,
2665      jint numScans,
2666      jintArray scanInfo,
2667      jintArray componentIds,
2668      jintArray HsamplingFactors,
2669      jintArray VsamplingFactors,
2670      jintArray QtableSelectors,
2671      jboolean haveMetadata,
2672      jint restartInterval) {
2673 
2674     struct jpeg_destination_mgr *dest;
2675     JSAMPROW scanLinePtr;
2676     int i, j;
2677     int pixelStride;
2678     unsigned char *in, *out, *pixelLimit, *scanLineLimit;
2679     unsigned int scanLineSize, pixelBufferSize;
2680     int targetLine;
2681     pixelBufferPtr pb;
2682     sun_jpeg_error_ptr jerr;
2683     jint *ids, *hfactors, *vfactors, *qsels;
2684     jsize qlen, hlen;
2685     int *scanptr;
2686     jint *scanData;
2687     jint *bandSize;
2688     int maxBandValue, halfMaxBandValue;
2689     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
2690     j_compress_ptr cinfo;
2691     UINT8** scale = NULL;
2692     boolean success = TRUE;
2693 
2694 
2695     /* verify the inputs */
2696 
2697     if (data == NULL) {
2698         JNU_ThrowByName(env,
2699                         "java/lang/IllegalStateException",
2700                         "Attempting to use writer after dispose()");
2701         return JNI_FALSE;
2702     }
2703 
2704     if ((buffer == NULL) ||
2705         (qtables == NULL) ||
2706         // H tables can be null if optimizing
2707         (componentIds == NULL) ||
2708         (HsamplingFactors == NULL) || (VsamplingFactors == NULL) ||
2709         (QtableSelectors == NULL) ||
2710         ((numScans != 0) && (scanInfo != NULL))) {
2711 
2712         JNU_ThrowNullPointerException(env, 0);
2713         return JNI_FALSE;
2714 
2715     }
2716 
2717     scanLineSize = destWidth * numBands;
2718     if ((inCs < 0) || (inCs > JCS_YCCK) ||
2719         (outCs < 0) || (outCs > JCS_YCCK) ||
2720         (numBands < 1) || (numBands > MAX_BANDS) ||
2721         (srcWidth < 0) ||
2722         (destWidth < 0) || (destWidth > srcWidth) ||
2723         (destHeight < 0) ||
2724         (stepX < 0) || (stepY < 0) ||
2725         ((INT_MAX / numBands) < destWidth))  /* destWidth causes an integer overflow */
2726     {
2727         JNU_ThrowByName(env, "javax/imageio/IIOException",
2728                         "Invalid argument to native writeImage");
2729         return JNI_FALSE;
2730     }
2731 
2732     if (stepX > srcWidth) {
2733         stepX = srcWidth;
2734     }
2735 
2736     bandSize = (*env)->GetIntArrayElements(env, bandSizes, NULL);
2737     CHECK_NULL_RETURN(bandSize, JNI_FALSE);
2738 
2739     for (i = 0; i < numBands; i++) {
2740         if (bandSize[i] <= 0 || bandSize[i] > JPEG_BAND_SIZE) {
2741             (*env)->ReleaseIntArrayElements(env, bandSizes,
2742                                             bandSize, JNI_ABORT);
2743             JNU_ThrowByName(env, "javax/imageio/IIOException", "Invalid Image");
2744             return JNI_FALSE;
2745         }
2746     }
2747 
2748     for (i = 0; i < numBands; i++) {
2749         if (bandSize[i] != JPEG_BAND_SIZE) {
2750             if (scale == NULL) {
2751                 scale = (UINT8**) calloc(numBands, sizeof(UINT8*));
2752 
2753                 if (scale == NULL) {
2754                     JNU_ThrowByName( env, "java/lang/OutOfMemoryError",
2755                                      "Writing JPEG Stream");
2756                     return JNI_FALSE;
2757                 }
2758             }
2759 
2760             maxBandValue = (1 << bandSize[i]) - 1;
2761 
2762             scale[i] = (UINT8*) malloc((maxBandValue + 1) * sizeof(UINT8));
2763 
2764             if (scale[i] == NULL) {
2765                 // Cleanup before throwing an out of memory exception
2766                 for (j = 0; j < i; j++) {
2767                     free(scale[j]);
2768                 }
2769                 free(scale);
2770                 JNU_ThrowByName( env, "java/lang/OutOfMemoryError",
2771                                  "Writing JPEG Stream");
2772                 return JNI_FALSE;
2773             }
2774 
2775             halfMaxBandValue = maxBandValue >> 1;
2776 
2777             for (j = 0; j <= maxBandValue; j++) {
2778                 scale[i][j] = (UINT8)
2779                     ((j*MAX_JPEG_BAND_VALUE + halfMaxBandValue)/maxBandValue);
2780             }
2781         }
2782     }
2783 
2784     (*env)->ReleaseIntArrayElements(env, bandSizes,
2785                                     bandSize, JNI_ABORT);
2786 
2787     cinfo = (j_compress_ptr) data->jpegObj;
2788     dest = cinfo->dest;
2789 
2790     /* Set the buffer as our PixelBuffer */
2791     pb = &data->pixelBuf;
2792 
2793     if (setPixelBuffer(env, pb, buffer) == NOT_OK) {
2794         if (scale != NULL) {
2795             for (i = 0; i < numBands; i++) {
2796                 if (scale[i] != NULL) {
2797                     free(scale[i]);
2798                 }
2799             }
2800             free(scale);
2801         }
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         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
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         if (scale != NULL) {
2831             for (i = 0; i < numBands; i++) {
2832                 if (scale[i] != NULL) {
2833                     free(scale[i]);
2834                 }
2835             }
2836             free(scale);
2837         }
2838 
2839         free(scanLinePtr);
2840         return data->abortFlag;
2841     }
2842 
2843     // set up parameters
2844     cinfo->image_width = destWidth;
2845     cinfo->image_height = destHeight;
2846     cinfo->input_components = numBands;
2847     cinfo->in_color_space = inCs;
2848 
2849     jpeg_set_defaults(cinfo);
2850 
2851     jpeg_set_colorspace(cinfo, outCs);
2852 
2853     cinfo->optimize_coding = optimize;
2854 
2855     cinfo->write_JFIF_header = FALSE;
2856     cinfo->write_Adobe_marker = FALSE;
2857     // copy componentIds
2858     ids = (*env)->GetIntArrayElements(env, componentIds, NULL);
2859     hfactors = (*env)->GetIntArrayElements(env, HsamplingFactors, NULL);
2860     vfactors = (*env)->GetIntArrayElements(env, VsamplingFactors, NULL);
2861     qsels = (*env)->GetIntArrayElements(env, QtableSelectors, NULL);
2862 
2863     if (ids && hfactors && vfactors && qsels) {
2864         for (i = 0; i < numBands; i++) {
2865             cinfo->comp_info[i].component_id = ids[i];
2866             cinfo->comp_info[i].h_samp_factor = hfactors[i];
2867             cinfo->comp_info[i].v_samp_factor = vfactors[i];
2868             cinfo->comp_info[i].quant_tbl_no = qsels[i];
2869         }
2870     } else {
2871         success = FALSE;
2872     }
2873 
2874     if (ids) {
2875         (*env)->ReleaseIntArrayElements(env, componentIds, ids, JNI_ABORT);
2876     }
2877     if (hfactors) {
2878         (*env)->ReleaseIntArrayElements(env, HsamplingFactors, hfactors, JNI_ABORT);
2879     }
2880     if (vfactors) {
2881         (*env)->ReleaseIntArrayElements(env, VsamplingFactors, vfactors, JNI_ABORT);
2882     }
2883     if (qsels) {
2884         (*env)->ReleaseIntArrayElements(env, QtableSelectors, qsels, JNI_ABORT);
2885     }
2886     if (!success) return data->abortFlag;
2887 
2888     jpeg_suppress_tables(cinfo, TRUE);  // Disable writing any current
2889 
2890     qlen = setQTables(env, (j_common_ptr) cinfo, qtables, writeDQT);
2891 
2892     if (!optimize) {
2893         // Set the h tables
2894         hlen = setHTables(env,
2895                           (j_common_ptr) cinfo,
2896                           DCHuffmanTables,
2897                           ACHuffmanTables,
2898                           writeDHT);
2899     }
2900 
2901     if (GET_ARRAYS(env, data,
2902                    (const JOCTET **)(&dest->next_output_byte)) == NOT_OK) {
2903         (*env)->ExceptionClear(env);
2904         JNU_ThrowByName(env,
2905                         "javax/imageio/IIOException",
2906                         "Array pin failed");
2907         return data->abortFlag;
2908     }
2909 
2910     data->streamBuf.suspendable = FALSE;
2911 
2912     if (progressive) {
2913         if (numScans == 0) { // then use default scans
2914             jpeg_simple_progression(cinfo);
2915         } else {
2916             cinfo->num_scans = numScans;
2917             // Copy the scanInfo to a local array
2918             // The following is copied from jpeg_simple_progression:
2919   /* Allocate space for script.
2920    * We need to put it in the permanent pool in case the application performs
2921    * multiple compressions without changing the settings.  To avoid a memory
2922    * leak if jpeg_simple_progression is called repeatedly for the same JPEG
2923    * object, we try to re-use previously allocated space, and we allocate
2924    * enough space to handle YCbCr even if initially asked for grayscale.
2925    */
2926             if (cinfo->script_space == NULL
2927                 || cinfo->script_space_size < numScans) {
2928                 cinfo->script_space_size = MAX(numScans, 10);
2929                 cinfo->script_space = (jpeg_scan_info *)
2930                     (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
2931                                                 JPOOL_PERMANENT,
2932                                                 cinfo->script_space_size
2933                                                 * sizeof(jpeg_scan_info));
2934             }
2935             cinfo->scan_info = cinfo->script_space;
2936             scanptr = (int *) cinfo->script_space;
2937             scanData = (*env)->GetIntArrayElements(env, scanInfo, NULL);
2938             CHECK_NULL_RETURN(scanData, data->abortFlag);
2939             // number of jints per scan is 9
2940             // We avoid a memcpy to handle different size ints
2941             for (i = 0; i < numScans*9; i++) {
2942                 scanptr[i] = scanData[i];
2943             }
2944             (*env)->ReleaseIntArrayElements(env, scanInfo,
2945                                             scanData, JNI_ABORT);
2946 
2947         }
2948     }
2949 
2950     cinfo->restart_interval = restartInterval;
2951 
2952 #ifdef DEBUG_IIO_JPEG
2953     printf("writer setup complete, starting compressor\n");
2954 #endif
2955 
2956     // start the compressor; tables must already be set
2957     jpeg_start_compress(cinfo, FALSE); // Leaves sent_table alone
2958 
2959     if (haveMetadata) {
2960         // Flush the buffer
2961         imageio_flush_destination(cinfo);
2962         // Call Java to write the metadata
2963         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2964         (*env)->CallVoidMethod(env,
2965                                this,
2966                                JPEGImageWriter_writeMetadataID);
2967         if ((*env)->ExceptionOccurred(env)
2968             || !GET_ARRAYS(env, data,
2969                            (const JOCTET **)(&dest->next_output_byte))) {
2970                 cinfo->err->error_exit((j_common_ptr) cinfo);
2971          }
2972     }
2973 
2974     targetLine = 0;
2975     pixelBufferSize = srcWidth * numBands;
2976     pixelStride = numBands * stepX;
2977 
2978     // for each line in destHeight
2979     while ((data->abortFlag == JNI_FALSE)
2980            && (cinfo->next_scanline < cinfo->image_height)) {
2981         // get the line from Java
2982         RELEASE_ARRAYS(env, data, (const JOCTET *)(dest->next_output_byte));
2983         (*env)->CallVoidMethod(env,
2984                                this,
2985                                JPEGImageWriter_grabPixelsID,
2986                                targetLine);
2987         if ((*env)->ExceptionOccurred(env)
2988             || !GET_ARRAYS(env, data,
2989                            (const JOCTET **)(&dest->next_output_byte))) {
2990                 cinfo->err->error_exit((j_common_ptr) cinfo);
2991          }
2992 
2993         // subsample it into our buffer
2994 
2995         in = data->pixelBuf.buf.bp;
2996         out = scanLinePtr;
2997         pixelLimit = in + ((pixelBufferSize > data->pixelBuf.byteBufferLength) ?
2998                            data->pixelBuf.byteBufferLength : pixelBufferSize);
2999         for (; (in < pixelLimit) && (out < scanLineLimit); in += pixelStride) {
3000             for (i = 0; i < numBands; i++) {
3001                 if (scale !=NULL && scale[i] != NULL) {
3002                     *out++ = scale[i][*(in+i)];
3003 #ifdef DEBUG_IIO_JPEG
3004                     if (in == data->pixelBuf.buf.bp){ // Just the first pixel
3005                         printf("in %d -> out %d, ", *(in+i), *(out-i-1));
3006                     }
3007 #endif
3008 
3009 #ifdef DEBUG_IIO_JPEG
3010                     if (in == data->pixelBuf.buf.bp){ // Just the first pixel
3011                         printf("\n");
3012                     }
3013 #endif
3014                 } else {
3015                     *out++ = *(in+i);
3016                 }
3017             }
3018         }
3019         // write it out
3020         jpeg_write_scanlines(cinfo, (JSAMPARRAY)&scanLinePtr, 1);
3021         targetLine += stepY;
3022     }
3023 
3024     /*
3025      * We are done, but we might not have done all the lines,
3026      * so use jpeg_abort instead of jpeg_finish_compress.
3027      */
3028     if (cinfo->next_scanline == cinfo->image_height) {
3029         jpeg_finish_compress(cinfo);  // Flushes buffer with term_dest
3030     } else {
3031         jpeg_abort((j_common_ptr)cinfo);
3032     }
3033 
3034     if (scale != NULL) {
3035         for (i = 0; i < numBands; i++) {
3036             if (scale[i] != NULL) {
3037                 free(scale[i]);
3038             }
3039         }
3040         free(scale);
3041     }
3042 
3043     free(scanLinePtr);
3044     RELEASE_ARRAYS(env, data, NULL);
3045     return data->abortFlag;
3046 }
3047 
3048 JNIEXPORT void JNICALL
3049 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_abortWrite
3050     (JNIEnv *env,
3051      jobject this,
3052      jlong ptr) {
3053 
3054     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
3055 
3056     if (data == NULL) {
3057         JNU_ThrowByName(env,
3058                         "java/lang/IllegalStateException",
3059                         "Attempting to use writer after dispose()");
3060         return;
3061     }
3062 
3063     imageio_abort(env, this, data);
3064 }
3065 
3066 JNIEXPORT void JNICALL
3067 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_resetWriter
3068     (JNIEnv *env,
3069      jobject this,
3070      jlong ptr) {
3071     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
3072     j_compress_ptr cinfo;
3073 
3074     if (data == NULL) {
3075         JNU_ThrowByName(env,
3076                         "java/lang/IllegalStateException",
3077                         "Attempting to use writer after dispose()");
3078         return;
3079     }
3080 
3081     cinfo = (j_compress_ptr) data->jpegObj;
3082 
3083     imageio_reset(env, (j_common_ptr) cinfo, data);
3084 
3085     /*
3086      * The tables have not been reset, and there is no way to do so
3087      * in IJG without leaking memory.  The only situation in which
3088      * this will cause a problem is if an image-only stream is written
3089      * with this object without initializing the correct tables first,
3090      * which should not be possible.
3091      */
3092 
3093     cinfo->dest->next_output_byte = NULL;
3094     cinfo->dest->free_in_buffer = 0;
3095 }
3096 
3097 JNIEXPORT void JNICALL
3098 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_disposeWriter
3099     (JNIEnv *env,
3100      jclass writer,
3101      jlong ptr) {
3102 
3103     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
3104     j_common_ptr info = destroyImageioData(env, data);
3105 
3106     imageio_dispose(info);
3107 }