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