| /* | |
| * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include "awt_parseImage.h" | |
| #include "imageInitIDs.h" | |
| #include "java_awt_Transparency.h" | |
| #include "java_awt_image_BufferedImage.h" | |
| #include "sun_awt_image_IntegerComponentRaster.h" | |
| #include "sun_awt_image_ImagingLib.h" | |
| #include "java_awt_color_ColorSpace.h" | |
| #include "awt_Mlib.h" | |
| #include "safe_alloc.h" | |
| #include "safe_math.h" | |
| static int setHints(JNIEnv *env, BufImageS_t *imageP); | |
| /* Parse the buffered image. All of the raster information is returned in the | |
| * imagePP structure. | |
| * | |
| * The handleCustom parameter specifies whether or not the caller | |
| * can use custom channels. If it is false and a custom channel | |
| * is encountered, the returned value will be 0 and all structures | |
| * will be deallocated. | |
| * | |
| * Return value: | |
| * -1: Exception | |
| * 0: Can't do it. | |
| * 1: Success | |
| */ | |
| int awt_parseImage(JNIEnv *env, jobject jimage, BufImageS_t **imagePP, | |
| int handleCustom) { | |
| BufImageS_t *imageP; | |
| int status; | |
| jobject jraster; | |
| jobject jcmodel; | |
| /* Make sure the image exists */ | |
| if (JNU_IsNull(env, jimage)) { | |
| JNU_ThrowNullPointerException(env, "null BufferedImage object"); | |
| return -1; | |
| } | |
| if ((imageP = (BufImageS_t *) calloc(1, sizeof(BufImageS_t))) == NULL) { | |
| JNU_ThrowOutOfMemoryError(env, "Out of memory"); | |
| return -1; | |
| } | |
| imageP->jimage = jimage; | |
| /* Retrieve the raster */ | |
| if ((jraster = (*env)->GetObjectField(env, jimage, | |
| g_BImgRasterID)) == NULL) { | |
| free((void *) imageP); | |
| JNU_ThrowNullPointerException(env, "null Raster object"); | |
| return 0; | |
| } | |
| /* Retrieve the image type */ | |
| imageP->imageType = (*env)->GetIntField(env, jimage, g_BImgTypeID); | |
| /* Parse the raster */ | |
| if ((status = awt_parseRaster(env, jraster, &imageP->raster)) <= 0) { | |
| free((void *)imageP); | |
| return status; | |
| } | |
| /* Retrieve the color model */ | |
| if ((jcmodel = (*env)->GetObjectField(env, jimage, g_BImgCMID)) == NULL) { | |
| free((void *) imageP); | |
| JNU_ThrowNullPointerException(env, "null Raster object"); | |
| return 0; | |
| } | |
| /* Parse the color model */ | |
| if ((status = awt_parseColorModel(env, jcmodel, imageP->imageType, | |
| &imageP->cmodel)) <= 0) { | |
| awt_freeParsedRaster(&imageP->raster, FALSE); | |
| free((void *)imageP); | |
| return 0; | |
| } | |
| /* Set hints */ | |
| if ((status = setHints(env, imageP)) <= 0) { | |
| awt_freeParsedImage(imageP, TRUE); | |
| return 0; | |
| } | |
| *imagePP = imageP; | |
| return status; | |
| } | |
| /* Verifies whether the channel offsets are sane and correspond to the type of | |
| * the raster. | |
| * | |
| * Return value: | |
| * 0: Failure: channel offsets are invalid | |
| * 1: Success | |
| */ | |
| static int checkChannelOffsets(RasterS_t *rasterP, int dataArrayLength) { | |
| int i, lastPixelOffset, lastScanOffset; | |
| switch (rasterP->rasterType) { | |
| case COMPONENT_RASTER_TYPE: | |
| if (!SAFE_TO_MULT(rasterP->height, rasterP->scanlineStride)) { | |
| return 0; | |
| } | |
| if (!SAFE_TO_MULT(rasterP->width, rasterP->pixelStride)) { | |
| return 0; | |
| } | |
| lastScanOffset = (rasterP->height - 1) * rasterP->scanlineStride; | |
| lastPixelOffset = (rasterP->width - 1) * rasterP->pixelStride; | |
| if (!SAFE_TO_ADD(lastPixelOffset, lastScanOffset)) { | |
| return 0; | |
| } | |
| lastPixelOffset += lastScanOffset; | |
| for (i = 0; i < rasterP->numDataElements; i++) { | |
| int off = rasterP->chanOffsets[i]; | |
| int size = lastPixelOffset + off; | |
| if (off < 0 || !SAFE_TO_ADD(lastPixelOffset, off)) { | |
| return 0; | |
| } | |
| if (size < lastPixelOffset || size >= dataArrayLength) { | |
| // an overflow, or insufficient buffer capacity | |
| return 0; | |
| } | |
| } | |
| return 1; | |
| case BANDED_RASTER_TYPE: | |
| // NB:caller does not support the banded rasters yet, | |
| // so this branch of the code must be re-defined in | |
| // order to provide valid criteria for the data offsets | |
| // verification, when/if banded rasters will be supported. | |
| // At the moment, we prohibit banded rasters as well. | |
| return 0; | |
| default: | |
| // PACKED_RASTER_TYPE: does not support channel offsets | |
| // UNKNOWN_RASTER_TYPE: should not be used, likely indicates an error | |
| return 0; | |
| } | |
| } | |
| /* Parse the raster. All of the raster information is returned in the | |
| * rasterP structure. | |
| * | |
| * Return value: | |
| * -1: Exception | |
| * 0: Can't do it (Custom channel) | |
| * 1: Success | |
| */ | |
| int awt_parseRaster(JNIEnv *env, jobject jraster, RasterS_t *rasterP) { | |
| jobject joffs = NULL; | |
| /* int status;*/ | |
| jclass singlePixelPackedSampleModelClass = NULL; | |
| jclass integerComponentRasterClass = NULL; | |
| jclass byteComponentRasterClass = NULL; | |
| jclass shortComponentRasterClass = NULL; | |
| jclass bytePackedRasterClass = NULL; | |
| if (JNU_IsNull(env, jraster)) { | |
| JNU_ThrowNullPointerException(env, "null Raster object"); | |
| return -1; | |
| } | |
| rasterP->jraster = jraster; | |
| rasterP->width = (*env)->GetIntField(env, jraster, g_RasterWidthID); | |
| rasterP->height = (*env)->GetIntField(env, jraster, g_RasterHeightID); | |
| rasterP->numDataElements = (*env)->GetIntField(env, jraster, | |
| g_RasterNumDataElementsID); | |
| rasterP->numBands = (*env)->GetIntField(env, jraster, | |
| g_RasterNumBandsID); | |
| rasterP->baseOriginX = (*env)->GetIntField(env, jraster, | |
| g_RasterBaseOriginXID); | |
| rasterP->baseOriginY = (*env)->GetIntField(env, jraster, | |
| g_RasterBaseOriginYID); | |
| rasterP->minX = (*env)->GetIntField(env, jraster, g_RasterMinXID); | |
| rasterP->minY = (*env)->GetIntField(env, jraster, g_RasterMinYID); | |
| rasterP->jsampleModel = (*env)->GetObjectField(env, jraster, | |
| g_RasterSampleModelID); | |
| if (JNU_IsNull(env, rasterP->jsampleModel)) { | |
| JNU_ThrowNullPointerException(env, "null Raster object"); | |
| return -1; | |
| } | |
| // make sure that the raster type is initialized | |
| rasterP->rasterType = UNKNOWN_RASTER_TYPE; | |
| if (rasterP->numBands <= 0 || | |
| rasterP->numBands > MAX_NUMBANDS) | |
| { | |
| /* | |
| * we can't handle such kind of rasters due to limitations | |
| * of SPPSampleModelS_t structure and expand/set methods. | |
| */ | |
| return 0; | |
| } | |
| rasterP->sppsm.isUsed = 0; | |
| singlePixelPackedSampleModelClass = (*env)->FindClass(env, | |
| "java/awt/image/SinglePixelPackedSampleModel"); | |
| CHECK_NULL_RETURN(singlePixelPackedSampleModelClass, -1); | |
| if ((*env)->IsInstanceOf(env, rasterP->jsampleModel, | |
| singlePixelPackedSampleModelClass)) { | |
| jobject jmask, joffs, jnbits; | |
| rasterP->sppsm.isUsed = 1; | |
| rasterP->sppsm.maxBitSize = (*env)->GetIntField(env, | |
| rasterP->jsampleModel, | |
| g_SPPSMmaxBitID); | |
| jmask = (*env)->GetObjectField(env, rasterP->jsampleModel, | |
| g_SPPSMmaskArrID); | |
| joffs = (*env)->GetObjectField(env, rasterP->jsampleModel, | |
| g_SPPSMmaskOffID); | |
| jnbits = (*env)->GetObjectField(env, rasterP->jsampleModel, | |
| g_SPPSMnBitsID); | |
| if (jmask == NULL || joffs == NULL || jnbits == NULL || | |
| rasterP->sppsm.maxBitSize < 0) | |
| { | |
| JNU_ThrowInternalError(env, "Can't grab SPPSM fields"); | |
| return -1; | |
| } | |
| (*env)->GetIntArrayRegion(env, jmask, 0, | |
| rasterP->numBands, rasterP->sppsm.maskArray); | |
| (*env)->GetIntArrayRegion(env, joffs, 0, | |
| rasterP->numBands, rasterP->sppsm.offsets); | |
| (*env)->GetIntArrayRegion(env, jnbits, 0, | |
| rasterP->numBands, rasterP->sppsm.nBits); | |
| } | |
| rasterP->baseRasterWidth = (*env)->GetIntField(env, rasterP->jsampleModel, | |
| g_SMWidthID); | |
| rasterP->baseRasterHeight = (*env)->GetIntField(env, | |
| rasterP->jsampleModel, | |
| g_SMHeightID); | |
| integerComponentRasterClass = (*env)->FindClass(env, "sun/awt/image/IntegerComponentRaster"); | |
| CHECK_NULL_RETURN(integerComponentRasterClass, -1); | |
| byteComponentRasterClass = (*env)->FindClass(env, "sun/awt/image/ByteComponentRaster"); | |
| CHECK_NULL_RETURN(byteComponentRasterClass, -1); | |
| shortComponentRasterClass = (*env)->FindClass(env,"sun/awt/image/ShortComponentRaster"); | |
| CHECK_NULL_RETURN(shortComponentRasterClass, -1); | |
| bytePackedRasterClass = (*env)->FindClass(env, "sun/awt/image/BytePackedRaster"); | |
| CHECK_NULL_RETURN(bytePackedRasterClass, -1); | |
| if ((*env)->IsInstanceOf(env, jraster, integerComponentRasterClass)){ | |
| rasterP->jdata = (*env)->GetObjectField(env, jraster, g_ICRdataID); | |
| rasterP->dataType = INT_DATA_TYPE; | |
| rasterP->dataSize = 4; | |
| rasterP->dataIsShared = TRUE; | |
| rasterP->rasterType = COMPONENT_RASTER_TYPE; | |
| rasterP->type = (*env)->GetIntField(env, jraster, g_ICRtypeID); | |
| rasterP->scanlineStride = (*env)->GetIntField(env, jraster, g_ICRscanstrID); | |
| rasterP->pixelStride = (*env)->GetIntField(env, jraster, g_ICRpixstrID); | |
| joffs = (*env)->GetObjectField(env, jraster, g_ICRdataOffsetsID); | |
| } | |
| else if ((*env)->IsInstanceOf(env, jraster, byteComponentRasterClass)){ | |
| rasterP->jdata = (*env)->GetObjectField(env, jraster, g_BCRdataID); | |
| rasterP->dataType = BYTE_DATA_TYPE; | |
| rasterP->dataSize = 1; | |
| rasterP->dataIsShared = TRUE; | |
| rasterP->rasterType = COMPONENT_RASTER_TYPE; | |
| rasterP->type = (*env)->GetIntField(env, jraster, g_BCRtypeID); | |
| rasterP->scanlineStride = (*env)->GetIntField(env, jraster, g_BCRscanstrID); | |
| rasterP->pixelStride = (*env)->GetIntField(env, jraster, g_BCRpixstrID); | |
| joffs = (*env)->GetObjectField(env, jraster, g_BCRdataOffsetsID); | |
| } | |
| else if ((*env)->IsInstanceOf(env, jraster, shortComponentRasterClass)){ | |
| rasterP->jdata = (*env)->GetObjectField(env, jraster, g_SCRdataID); | |
| rasterP->dataType = SHORT_DATA_TYPE; | |
| rasterP->dataSize = 2; | |
| rasterP->dataIsShared = TRUE; | |
| rasterP->rasterType = COMPONENT_RASTER_TYPE; | |
| rasterP->type = (*env)->GetIntField(env, jraster, g_SCRtypeID); | |
| rasterP->scanlineStride = (*env)->GetIntField(env, jraster, g_SCRscanstrID); | |
| rasterP->pixelStride = (*env)->GetIntField(env, jraster, g_SCRpixstrID); | |
| joffs = (*env)->GetObjectField(env, jraster, g_SCRdataOffsetsID); | |
| } | |
| else if ((*env)->IsInstanceOf(env, jraster, bytePackedRasterClass)){ | |
| rasterP->rasterType = PACKED_RASTER_TYPE; | |
| rasterP->dataType = BYTE_DATA_TYPE; | |
| rasterP->dataSize = 1; | |
| rasterP->scanlineStride = (*env)->GetIntField(env, jraster, g_BPRscanstrID); | |
| rasterP->pixelStride = (*env)->GetIntField(env, jraster, g_BPRpixstrID); | |
| rasterP->jdata = (*env)->GetObjectField(env, jraster, g_BPRdataID); | |
| rasterP->type = (*env)->GetIntField(env, jraster, g_BPRtypeID); | |
| rasterP->chanOffsets = NULL; | |
| if (SAFE_TO_ALLOC_2(rasterP->numDataElements, sizeof(jint))) { | |
| rasterP->chanOffsets = | |
| (jint *)malloc(rasterP->numDataElements * sizeof(jint)); | |
| } | |
| if (rasterP->chanOffsets == NULL) { | |
| /* Out of memory */ | |
| JNU_ThrowOutOfMemoryError(env, "Out of memory"); | |
| return -1; | |
| } | |
| rasterP->chanOffsets[0] = (*env)->GetIntField(env, jraster, g_BPRdataBitOffsetID); | |
| rasterP->dataType = BYTE_DATA_TYPE; | |
| } | |
| else { | |
| rasterP->type = sun_awt_image_IntegerComponentRaster_TYPE_CUSTOM; | |
| rasterP->dataType = UNKNOWN_DATA_TYPE; | |
| rasterP->rasterType = UNKNOWN_RASTER_TYPE; | |
| rasterP->chanOffsets = NULL; | |
| /* Custom raster */ | |
| return 0; | |
| } | |
| // do basic validation of the raster structure | |
| if (rasterP->width <= 0 || rasterP->height <= 0 || | |
| rasterP->pixelStride <= 0 || rasterP->scanlineStride <= 0) | |
| { | |
| // invalid raster | |
| return -1; | |
| } | |
| // channel (data) offsets | |
| switch (rasterP->rasterType) { | |
| case COMPONENT_RASTER_TYPE: | |
| case BANDED_RASTER_TYPE: // note that this routine does not support banded rasters at the moment | |
| // get channel (data) offsets | |
| rasterP->chanOffsets = NULL; | |
| if (SAFE_TO_ALLOC_2(rasterP->numDataElements, sizeof(jint))) { | |
| rasterP->chanOffsets = | |
| (jint *)malloc(rasterP->numDataElements * sizeof(jint)); | |
| } | |
| if (rasterP->chanOffsets == NULL) { | |
| /* Out of memory */ | |
| JNU_ThrowOutOfMemoryError(env, "Out of memory"); | |
| return -1; | |
| } | |
| (*env)->GetIntArrayRegion(env, joffs, 0, rasterP->numDataElements, | |
| rasterP->chanOffsets); | |
| if (rasterP->jdata == NULL) { | |
| // unable to verify the raster | |
| return -1; | |
| } | |
| // verify whether channel offsets look sane | |
| if (!checkChannelOffsets(rasterP, (*env)->GetArrayLength(env, rasterP->jdata))) { | |
| return -1; | |
| } | |
| break; | |
| default: | |
| ; // PACKED_RASTER_TYPE does not use the channel offsets. | |
| } | |
| /* additional check for sppsm fields validity: make sure that | |
| * size of raster samples doesn't exceed the data type capacity. | |
| */ | |
| if (rasterP->dataType > UNKNOWN_DATA_TYPE && /* data type has been recognized */ | |
| rasterP->sppsm.maxBitSize > 0 && /* raster has SPP sample model */ | |
| rasterP->sppsm.maxBitSize > (rasterP->dataSize * 8)) | |
| { | |
| JNU_ThrowInternalError(env, "Raster samples are too big"); | |
| return -1; | |
| } | |
| #if 0 | |
| fprintf(stderr,"---------------------\n"); | |
| fprintf(stderr,"Width : %d\n",rasterP->width); | |
| fprintf(stderr,"Height : %d\n",rasterP->height); | |
| fprintf(stderr,"X : %d\n",rasterP->x); | |
| fprintf(stderr,"Y : %d\n",rasterP->y); | |
| fprintf(stderr,"numC : %d\n",rasterP->numDataElements); | |
| fprintf(stderr,"SS : %d\n",rasterP->scanlineStride); | |
| fprintf(stderr,"PS : %d\n",rasterP->pixelStride); | |
| fprintf(stderr,"CO : %d\n",rasterP->chanOffsets); | |
| fprintf(stderr,"shared?: %d\n",rasterP->dataIsShared); | |
| fprintf(stderr,"RasterT: %d\n",rasterP->rasterType); | |
| fprintf(stderr,"DataT : %d\n",rasterP->dataType); | |
| fprintf(stderr,"---------------------\n"); | |
| #endif | |
| return 1; | |
| } | |
| static int getColorModelType(JNIEnv *env, jobject jcmodel) { | |
| jclass colorModelClass; | |
| colorModelClass = (*env)->FindClass(env, | |
| "java/awt/image/IndexColorModel"); | |
| CHECK_NULL_RETURN(colorModelClass, UNKNOWN_CM_TYPE); | |
| if ((*env)->IsInstanceOf(env, jcmodel, colorModelClass)) | |
| { | |
| return INDEX_CM_TYPE; | |
| } | |
| colorModelClass = (*env)->FindClass(env, | |
| "java/awt/image/PackedColorModel"); | |
| CHECK_NULL_RETURN(colorModelClass, UNKNOWN_CM_TYPE); | |
| if ((*env)->IsInstanceOf(env, jcmodel, colorModelClass)) | |
| { | |
| colorModelClass = (*env)->FindClass(env, | |
| "java/awt/image/DirectColorModel"); | |
| CHECK_NULL_RETURN(colorModelClass, UNKNOWN_CM_TYPE); | |
| if ((*env)->IsInstanceOf(env, jcmodel, colorModelClass)) { | |
| return DIRECT_CM_TYPE; | |
| } | |
| else { | |
| return PACKED_CM_TYPE; | |
| } | |
| } | |
| colorModelClass = (*env)->FindClass(env, | |
| "java/awt/image/ComponentColorModel"); | |
| CHECK_NULL_RETURN(colorModelClass, UNKNOWN_CM_TYPE); | |
| if ((*env)->IsInstanceOf(env, jcmodel, colorModelClass)) | |
| { | |
| return COMPONENT_CM_TYPE; | |
| } | |
| return UNKNOWN_CM_TYPE; | |
| } | |
| int awt_parseColorModel (JNIEnv *env, jobject jcmodel, int imageType, | |
| ColorModelS_t *cmP) { | |
| /*jmethodID jID; */ | |
| jobject jnBits; | |
| jsize nBitsLength; | |
| int i; | |
| static jobject s_jdefCM = NULL; | |
| cmP->nBits = NULL; | |
| if (JNU_IsNull(env, jcmodel)) { | |
| JNU_ThrowNullPointerException(env, "null ColorModel object"); | |
| return -1; | |
| } | |
| cmP->jcmodel = jcmodel; | |
| cmP->jcspace = (*env)->GetObjectField(env, jcmodel, g_CMcspaceID); | |
| cmP->numComponents = (*env)->GetIntField(env, jcmodel, | |
| g_CMnumComponentsID); | |
| cmP->supportsAlpha = (*env)->GetBooleanField(env, jcmodel, | |
| g_CMsuppAlphaID); | |
| cmP->isAlphaPre = (*env)->GetBooleanField(env,jcmodel, | |
| g_CMisAlphaPreID); | |
| cmP->transparency = (*env)->GetIntField(env, jcmodel, | |
| g_CMtransparencyID); | |
| jnBits = (*env)->GetObjectField(env, jcmodel, g_CMnBitsID); | |
| if (jnBits == NULL) { | |
| JNU_ThrowNullPointerException(env, "null nBits structure in CModel"); | |
| return -1; | |
| } | |
| nBitsLength = (*env)->GetArrayLength(env, jnBits); | |
| if (nBitsLength != cmP->numComponents) { | |
| // invalid number of components? | |
| return -1; | |
| } | |
| if (SAFE_TO_ALLOC_2(cmP->numComponents, sizeof(jint))) { | |
| cmP->nBits = (jint *)malloc(cmP->numComponents * sizeof(jint)); | |
| } | |
| if (cmP->nBits == NULL){ | |
| JNU_ThrowOutOfMemoryError(env, "Out of memory"); | |
| return -1; | |
| } | |
| (*env)->GetIntArrayRegion(env, jnBits, 0, cmP->numComponents, | |
| cmP->nBits); | |
| cmP->maxNbits = 0; | |
| for (i=0; i < cmP->numComponents; i++) { | |
| if (cmP->maxNbits < cmP->nBits[i]) { | |
| cmP->maxNbits = cmP->nBits[i]; | |
| } | |
| } | |
| cmP->is_sRGB = (*env)->GetBooleanField(env, cmP->jcmodel, g_CMis_sRGBID); | |
| cmP->csType = (*env)->GetIntField(env, cmP->jcmodel, g_CMcsTypeID); | |
| cmP->cmType = getColorModelType(env, jcmodel); | |
| if ((*env)->ExceptionCheck(env)) { | |
| goto cleanup; | |
| } | |
| cmP->isDefaultCM = FALSE; | |
| cmP->isDefaultCompatCM = FALSE; | |
| /* look for standard cases */ | |
| if (imageType == java_awt_image_BufferedImage_TYPE_INT_ARGB) { | |
| cmP->isDefaultCM = TRUE; | |
| cmP->isDefaultCompatCM = TRUE; | |
| } else if (imageType == java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE || | |
| imageType == java_awt_image_BufferedImage_TYPE_INT_RGB || | |
| imageType == java_awt_image_BufferedImage_TYPE_INT_BGR || | |
| imageType == java_awt_image_BufferedImage_TYPE_4BYTE_ABGR || | |
| imageType == java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE) | |
| { | |
| cmP->isDefaultCompatCM = TRUE; | |
| } | |
| else { | |
| /* Figure out if this is the default CM */ | |
| if (s_jdefCM == NULL) { | |
| jobject defCM; | |
| jclass jcm = (*env)->FindClass(env, "java/awt/image/ColorModel"); | |
| if (jcm == NULL) { | |
| goto cleanup; | |
| } | |
| defCM = (*env)->CallStaticObjectMethod(env, jcm, | |
| g_CMgetRGBdefaultMID, NULL); | |
| if ((*env)->ExceptionCheck(env)) { | |
| (*env)->ExceptionClear(env); | |
| JNU_ThrowNullPointerException(env, "Unable to find default CM"); | |
| goto cleanup; | |
| } | |
| s_jdefCM = (*env)->NewGlobalRef(env, defCM); | |
| if (defCM == NULL || s_jdefCM == NULL) { | |
| (*env)->ExceptionClear(env); | |
| JNU_ThrowNullPointerException(env, "Unable to find default CM"); | |
| goto cleanup; | |
| } | |
| } | |
| cmP->isDefaultCM = ((*env)->IsSameObject(env, s_jdefCM, jcmodel)); | |
| cmP->isDefaultCompatCM = cmP->isDefaultCM; | |
| } | |
| /* check whether image attributes correspond to default cm */ | |
| if (cmP->isDefaultCompatCM) { | |
| if (cmP->csType != java_awt_color_ColorSpace_TYPE_RGB || | |
| !cmP->is_sRGB) | |
| { | |
| goto cleanup; | |
| } | |
| for (i = 0; i < cmP->numComponents; i++) { | |
| if (cmP->nBits[i] != 8) { | |
| goto cleanup; | |
| } | |
| } | |
| } | |
| /* Get index color model attributes */ | |
| if (imageType == java_awt_image_BufferedImage_TYPE_BYTE_INDEXED || | |
| cmP->cmType == INDEX_CM_TYPE) | |
| { | |
| cmP->transIdx = (*env)->GetIntField(env, jcmodel, g_ICMtransIdxID); | |
| cmP->mapSize = (*env)->GetIntField(env, jcmodel, g_ICMmapSizeID); | |
| cmP->jrgb = (*env)->GetObjectField(env, jcmodel, g_ICMrgbID); | |
| if (cmP->transIdx == -1) { | |
| /* Need to find the transparent index */ | |
| int *rgb = (int *) (*env)->GetPrimitiveArrayCritical(env, | |
| cmP->jrgb, | |
| NULL); | |
| if (rgb == NULL) { | |
| goto cleanup; | |
| } | |
| for (i=0; i < cmP->mapSize; i++) { | |
| if ((rgb[i]&0xff000000) == 0) { | |
| cmP->transIdx = i; | |
| break; | |
| } | |
| } | |
| (*env)->ReleasePrimitiveArrayCritical(env, cmP->jrgb, rgb, | |
| JNI_ABORT); | |
| if (cmP->transIdx == -1) { | |
| /* Now what? No transparent pixel... */ | |
| cmP->transIdx = 0; | |
| } | |
| } | |
| } | |
| return 1; | |
| cleanup: | |
| free(cmP->nBits); | |
| return -1; | |
| } | |
| void awt_freeParsedRaster(RasterS_t *rasterP, int freeRasterP) { | |
| if (rasterP->chanOffsets) { | |
| free((void *) rasterP->chanOffsets); | |
| } | |
| if (freeRasterP) { | |
| free((void *) rasterP); | |
| } | |
| } | |
| void awt_freeParsedImage(BufImageS_t *imageP, int freeImageP) { | |
| if (imageP->hints.colorOrder) { | |
| free ((void *) imageP->hints.colorOrder); | |
| } | |
| if (imageP->cmodel.nBits) { | |
| free ((void *) imageP->cmodel.nBits); | |
| } | |
| /* Free the raster */ | |
| awt_freeParsedRaster(&imageP->raster, FALSE); | |
| if (freeImageP) { | |
| free((void *) imageP); | |
| } | |
| } | |
| static void | |
| awt_getBIColorOrder(int type, int *colorOrder) { | |
| switch(type) { | |
| case java_awt_image_BufferedImage_TYPE_INT_ARGB: | |
| case java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE: | |
| #ifdef _LITTLE_ENDIAN | |
| colorOrder[0] = 2; | |
| colorOrder[1] = 1; | |
| colorOrder[2] = 0; | |
| colorOrder[3] = 3; | |
| #else | |
| colorOrder[0] = 1; | |
| colorOrder[1] = 2; | |
| colorOrder[2] = 3; | |
| colorOrder[3] = 0; | |
| #endif | |
| break; | |
| case java_awt_image_BufferedImage_TYPE_INT_BGR: | |
| #ifdef _LITTLE_ENDIAN | |
| colorOrder[0] = 0; | |
| colorOrder[1] = 1; | |
| colorOrder[2] = 2; | |
| #else | |
| colorOrder[0] = 3; | |
| colorOrder[1] = 2; | |
| colorOrder[2] = 1; | |
| #endif | |
| break; | |
| case java_awt_image_BufferedImage_TYPE_INT_RGB: | |
| #ifdef _LITTLE_ENDIAN | |
| colorOrder[0] = 2; | |
| colorOrder[1] = 1; | |
| colorOrder[2] = 0; | |
| #else | |
| colorOrder[0] = 1; | |
| colorOrder[1] = 2; | |
| colorOrder[2] = 3; | |
| #endif | |
| break; | |
| case java_awt_image_BufferedImage_TYPE_4BYTE_ABGR: | |
| case java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE: | |
| colorOrder[0] = 3; | |
| colorOrder[1] = 2; | |
| colorOrder[2] = 1; | |
| colorOrder[3] = 0; | |
| break; | |
| case java_awt_image_BufferedImage_TYPE_3BYTE_BGR: | |
| colorOrder[0] = 2; | |
| colorOrder[1] = 1; | |
| colorOrder[2] = 0; | |
| break; | |
| case java_awt_image_BufferedImage_TYPE_USHORT_565_RGB: | |
| case java_awt_image_BufferedImage_TYPE_USHORT_555_RGB: | |
| colorOrder[0] = 0; | |
| colorOrder[1] = 1; | |
| colorOrder[2] = 2; | |
| break; | |
| case java_awt_image_BufferedImage_TYPE_BYTE_GRAY: | |
| case java_awt_image_BufferedImage_TYPE_USHORT_GRAY: | |
| case java_awt_image_BufferedImage_TYPE_BYTE_BINARY: | |
| case java_awt_image_BufferedImage_TYPE_BYTE_INDEXED: | |
| colorOrder[0] = 0; | |
| break; | |
| } | |
| } | |
| static int | |
| setHints(JNIEnv *env, BufImageS_t *imageP) { | |
| HintS_t *hintP = &imageP->hints; | |
| RasterS_t *rasterP = &imageP->raster; | |
| ColorModelS_t *cmodelP = &imageP->cmodel; | |
| int imageType = imageP->imageType; | |
| // check whether raster and color model are compatible | |
| if (cmodelP->numComponents != rasterP->numBands) { | |
| if (cmodelP->cmType != INDEX_CM_TYPE) { | |
| return -1; | |
| } | |
| } | |
| hintP->numChans = imageP->cmodel.numComponents; | |
| hintP->colorOrder = NULL; | |
| if (SAFE_TO_ALLOC_2(hintP->numChans, sizeof(int))) { | |
| hintP->colorOrder = (int *)malloc(hintP->numChans * sizeof(int)); | |
| } | |
| if (hintP->colorOrder == NULL) { | |
| JNU_ThrowOutOfMemoryError(env, "Out of memory"); | |
| return -1; | |
| } | |
| if (imageType != java_awt_image_BufferedImage_TYPE_CUSTOM) { | |
| awt_getBIColorOrder(imageType, hintP->colorOrder); | |
| } | |
| if (imageType == java_awt_image_BufferedImage_TYPE_INT_ARGB || | |
| imageType == java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE || | |
| imageType == java_awt_image_BufferedImage_TYPE_INT_RGB) | |
| { | |
| hintP->channelOffset = rasterP->chanOffsets[0]; | |
| /* These hints are #bytes */ | |
| hintP->dataOffset = hintP->channelOffset*rasterP->dataSize; | |
| hintP->sStride = rasterP->scanlineStride*rasterP->dataSize; | |
| hintP->pStride = rasterP->pixelStride*rasterP->dataSize; | |
| hintP->packing = BYTE_INTERLEAVED; | |
| } else if (imageType ==java_awt_image_BufferedImage_TYPE_4BYTE_ABGR || | |
| imageType==java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE|| | |
| imageType == java_awt_image_BufferedImage_TYPE_3BYTE_BGR || | |
| imageType == java_awt_image_BufferedImage_TYPE_INT_BGR) | |
| { | |
| if (imageType == java_awt_image_BufferedImage_TYPE_INT_BGR) { | |
| hintP->channelOffset = rasterP->chanOffsets[0]; | |
| } | |
| else { | |
| hintP->channelOffset = rasterP->chanOffsets[hintP->numChans-1]; | |
| } | |
| hintP->dataOffset = hintP->channelOffset*rasterP->dataSize; | |
| hintP->sStride = rasterP->scanlineStride*rasterP->dataSize; | |
| hintP->pStride = rasterP->pixelStride*rasterP->dataSize; | |
| hintP->packing = BYTE_INTERLEAVED; | |
| } else if (imageType==java_awt_image_BufferedImage_TYPE_USHORT_565_RGB || | |
| imageType==java_awt_image_BufferedImage_TYPE_USHORT_555_RGB) { | |
| hintP->needToExpand = TRUE; | |
| hintP->expandToNbits = 8; | |
| hintP->packing = PACKED_SHORT_INTER; | |
| } else if (cmodelP->cmType == INDEX_CM_TYPE) { | |
| int i; | |
| hintP->numChans = 1; | |
| hintP->channelOffset = rasterP->chanOffsets[0]; | |
| hintP->dataOffset = hintP->channelOffset*rasterP->dataSize; | |
| hintP->sStride = rasterP->scanlineStride*rasterP->dataSize; | |
| hintP->pStride = rasterP->pixelStride*rasterP->dataSize; | |
| switch(rasterP->dataType ) { | |
| case BYTE_DATA_TYPE: | |
| if (rasterP->rasterType == PACKED_RASTER_TYPE) { | |
| hintP->needToExpand = TRUE; | |
| hintP->expandToNbits = 8; | |
| hintP->packing = BYTE_PACKED_BAND; | |
| } | |
| else { | |
| hintP->packing = BYTE_SINGLE_BAND; | |
| } | |
| break; | |
| case SHORT_DATA_TYPE: | |
| hintP->packing = SHORT_SINGLE_BAND; | |
| break; | |
| case INT_DATA_TYPE: | |
| default: | |
| hintP->packing = UNKNOWN_PACKING; | |
| break; | |
| } | |
| for (i=0; i < hintP->numChans; i++) { | |
| hintP->colorOrder[i] = i; | |
| } | |
| } | |
| else if (cmodelP->cmType == COMPONENT_CM_TYPE) { | |
| /* Figure out if it is interleaved */ | |
| int bits=1; | |
| int i; | |
| int low = rasterP->chanOffsets[0]; | |
| int diff; | |
| int banded = 0; | |
| for (i=1; i < hintP->numChans; i++) { | |
| if (rasterP->chanOffsets[i] < low) { | |
| low = rasterP->chanOffsets[i]; | |
| } | |
| } | |
| for (i=1; i < hintP->numChans; i++) { | |
| diff = rasterP->chanOffsets[i]-low; | |
| if (diff < hintP->numChans) { | |
| if (bits & (1<<diff)) { | |
| /* Overlapping samples */ | |
| /* Could just copy */ | |
| return -1; | |
| } | |
| bits |= (1<<diff); | |
| } | |
| else if (diff >= rasterP->width) { | |
| banded = 1; | |
| } | |
| /* Ignore the case if bands are overlapping */ | |
| } | |
| hintP->channelOffset = low; | |
| hintP->dataOffset = low*rasterP->dataSize; | |
| hintP->sStride = rasterP->scanlineStride*rasterP->dataSize; | |
| hintP->pStride = rasterP->pixelStride*rasterP->dataSize; | |
| switch(rasterP->dataType) { | |
| case BYTE_DATA_TYPE: | |
| hintP->packing = BYTE_COMPONENTS; | |
| break; | |
| case SHORT_DATA_TYPE: | |
| hintP->packing = SHORT_COMPONENTS; | |
| break; | |
| default: | |
| /* Don't handle any other case */ | |
| return -1; | |
| } | |
| if (bits == ((1<<hintP->numChans)-1)) { | |
| hintP->packing |= INTERLEAVED; | |
| for (i=0; i < hintP->numChans; i++) { | |
| hintP->colorOrder[rasterP->chanOffsets[i]-low] = i; | |
| } | |
| } | |
| else if (banded == 1) { | |
| int bandSize = rasterP->width*rasterP->height; | |
| hintP->packing |= BANDED; | |
| for (i=0; i < hintP->numChans; i++) { | |
| /* REMIND: Not necessarily correct */ | |
| hintP->colorOrder[(rasterP->chanOffsets[i]-low)%bandSize] = i; | |
| } | |
| } | |
| else { | |
| return -1; | |
| } | |
| } | |
| else if (cmodelP->cmType == DIRECT_CM_TYPE || cmodelP->cmType == PACKED_CM_TYPE) { | |
| int i; | |
| /* do some sanity check first: make sure that | |
| * - sample model is SinglePixelPackedSampleModel | |
| * - number of bands in the raster corresponds to the number | |
| * of color components in the color model | |
| */ | |
| if (!rasterP->sppsm.isUsed || | |
| rasterP->numBands != cmodelP->numComponents) | |
| { | |
| /* given raster is not compatible with the color model, | |
| * so the operation has to be aborted. | |
| */ | |
| return -1; | |
| } | |
| if (cmodelP->maxNbits > 8) { | |
| hintP->needToExpand = TRUE; | |
| hintP->expandToNbits = cmodelP->maxNbits; | |
| } | |
| else { | |
| for (i=0; i < rasterP->numBands; i++) { | |
| if (!(rasterP->sppsm.offsets[i] % 8)) { | |
| hintP->needToExpand = TRUE; | |
| hintP->expandToNbits = 8; | |
| break; | |
| } | |
| else { | |
| hintP->colorOrder[i] = rasterP->sppsm.offsets[i]>>3; | |
| } | |
| } | |
| } | |
| hintP->channelOffset = rasterP->chanOffsets[0]; | |
| hintP->dataOffset = hintP->channelOffset*rasterP->dataSize; | |
| hintP->sStride = rasterP->scanlineStride*rasterP->dataSize; | |
| hintP->pStride = rasterP->pixelStride*rasterP->dataSize; | |
| if (hintP->needToExpand) { | |
| switch(rasterP->dataType) { | |
| case BYTE_DATA_TYPE: | |
| hintP->packing = PACKED_BYTE_INTER; | |
| break; | |
| case SHORT_DATA_TYPE: | |
| hintP->packing = PACKED_SHORT_INTER; | |
| break; | |
| case INT_DATA_TYPE: | |
| hintP->packing = PACKED_INT_INTER; | |
| break; | |
| default: | |
| /* Don't know what it is */ | |
| return -1; | |
| } | |
| } | |
| else { | |
| hintP->packing = BYTE_INTERLEAVED; | |
| } | |
| } | |
| else { | |
| /* REMIND: Need to handle more cases */ | |
| return -1; | |
| } | |
| return 1; | |
| } | |
| #define MAX_TO_GRAB (10240) | |
| typedef union { | |
| void *pv; | |
| unsigned char *pb; | |
| unsigned short *ps; | |
| } PixelData_t; | |
| int awt_getPixels(JNIEnv *env, RasterS_t *rasterP, void *bufferP) { | |
| const int w = rasterP->width; | |
| const int h = rasterP->height; | |
| const int numBands = rasterP->numBands; | |
| int y; | |
| int i; | |
| int maxLines; | |
| jobject jsm; | |
| int off = 0; | |
| jarray jdata = NULL; | |
| jobject jdatabuffer; | |
| int *dataP; | |
| int maxSamples; | |
| PixelData_t p; | |
| if (bufferP == NULL) { | |
| return -1; | |
| } | |
| if (rasterP->dataType != BYTE_DATA_TYPE && | |
| rasterP->dataType != SHORT_DATA_TYPE) | |
| { | |
| return -1; | |
| } | |
| p.pv = bufferP; | |
| if (!SAFE_TO_MULT(w, numBands)) { | |
| return -1; | |
| } | |
| maxSamples = w * numBands; | |
| maxLines = maxSamples > MAX_TO_GRAB ? 1 : (MAX_TO_GRAB / maxSamples); | |
| if (maxLines > h) { | |
| maxLines = h; | |
| } | |
| if (!SAFE_TO_MULT(maxSamples, maxLines)) { | |
| return -1; | |
| } | |
| maxSamples *= maxLines; | |
| jsm = (*env)->GetObjectField(env, rasterP->jraster, g_RasterSampleModelID); | |
| jdatabuffer = (*env)->GetObjectField(env, rasterP->jraster, | |
| g_RasterDataBufferID); | |
| jdata = (*env)->NewIntArray(env, maxSamples); | |
| if (JNU_IsNull(env, jdata)) { | |
| (*env)->ExceptionClear(env); | |
| JNU_ThrowOutOfMemoryError(env, "Out of Memory"); | |
| return -1; | |
| } | |
| for (y = 0; y < h; y += maxLines) { | |
| if (y + maxLines > h) { | |
| maxLines = h - y; | |
| maxSamples = w * numBands * maxLines; | |
| } | |
| (*env)->CallObjectMethod(env, jsm, g_SMGetPixelsMID, | |
| 0, y, w, | |
| maxLines, jdata, jdatabuffer); | |
| if ((*env)->ExceptionCheck(env)) { | |
| (*env)->DeleteLocalRef(env, jdata); | |
| return -1; | |
| } | |
| dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata, | |
| NULL); | |
| if (dataP == NULL) { | |
| (*env)->DeleteLocalRef(env, jdata); | |
| return -1; | |
| } | |
| switch (rasterP->dataType) { | |
| case BYTE_DATA_TYPE: | |
| for (i = 0; i < maxSamples; i ++) { | |
| p.pb[off++] = (unsigned char) dataP[i]; | |
| } | |
| break; | |
| case SHORT_DATA_TYPE: | |
| for (i = 0; i < maxSamples; i ++) { | |
| p.ps[off++] = (unsigned short) dataP[i]; | |
| } | |
| break; | |
| } | |
| (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP, | |
| JNI_ABORT); | |
| } | |
| (*env)->DeleteLocalRef(env, jdata); | |
| return 1; | |
| } | |
| int awt_setPixels(JNIEnv *env, RasterS_t *rasterP, void *bufferP) { | |
| const int w = rasterP->width; | |
| const int h = rasterP->height; | |
| const int numBands = rasterP->numBands; | |
| int y; | |
| int i; | |
| int maxLines; | |
| jobject jsm; | |
| int off = 0; | |
| jarray jdata = NULL; | |
| jobject jdatabuffer; | |
| int *dataP; | |
| int maxSamples; | |
| PixelData_t p; | |
| if (bufferP == NULL) { | |
| return -1; | |
| } | |
| if (rasterP->dataType != BYTE_DATA_TYPE && | |
| rasterP->dataType != SHORT_DATA_TYPE) | |
| { | |
| return -1; | |
| } | |
| p.pv = bufferP; | |
| if (!SAFE_TO_MULT(w, numBands)) { | |
| return -1; | |
| } | |
| maxSamples = w * numBands; | |
| maxLines = maxSamples > MAX_TO_GRAB ? 1 : (MAX_TO_GRAB / maxSamples); | |
| if (maxLines > h) { | |
| maxLines = h; | |
| } | |
| if (!SAFE_TO_MULT(maxSamples, maxLines)) { | |
| return -1; | |
| } | |
| maxSamples *= maxLines; | |
| jsm = (*env)->GetObjectField(env, rasterP->jraster, g_RasterSampleModelID); | |
| jdatabuffer = (*env)->GetObjectField(env, rasterP->jraster, | |
| g_RasterDataBufferID); | |
| jdata = (*env)->NewIntArray(env, maxSamples); | |
| if (JNU_IsNull(env, jdata)) { | |
| (*env)->ExceptionClear(env); | |
| JNU_ThrowOutOfMemoryError(env, "Out of Memory"); | |
| return -1; | |
| } | |
| for (y = 0; y < h; y += maxLines) { | |
| if (y + maxLines > h) { | |
| maxLines = h - y; | |
| maxSamples = w * numBands * maxLines; | |
| } | |
| dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata, | |
| NULL); | |
| if (dataP == NULL) { | |
| (*env)->DeleteLocalRef(env, jdata); | |
| return -1; | |
| } | |
| switch (rasterP->dataType) { | |
| case BYTE_DATA_TYPE: | |
| for (i = 0; i < maxSamples; i ++) { | |
| dataP[i] = p.pb[off++]; | |
| } | |
| break; | |
| case SHORT_DATA_TYPE: | |
| for (i = 0; i < maxSamples; i ++) { | |
| dataP[i] = p.ps[off++]; | |
| } | |
| break; | |
| } | |
| (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP, | |
| JNI_ABORT); | |
| (*env)->CallVoidMethod(env, jsm, g_SMSetPixelsMID, | |
| 0, y, w, | |
| maxLines, jdata, jdatabuffer); | |
| if ((*env)->ExceptionCheck(env)) { | |
| (*env)->DeleteLocalRef(env, jdata); | |
| return -1; | |
| } | |
| } | |
| (*env)->DeleteLocalRef(env, jdata); | |
| return 1; | |
| } |