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