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