view src/share/native/sun/awt/image/awt_parseImage.c @ 4938:f50ec548334f

8012597: Better image channel verification Reviewed-by: ahgross, vadim, prr
author bae
date Tue, 30 Apr 2013 04:20:46 +0400
parents 206248632db3
children
line wrap: on
line source

/*
 * Copyright (c) 1997, 2010, 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;*/

    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;

    if ((*env)->IsInstanceOf(env, rasterP->jsampleModel,
       (*env)->FindClass(env,"java/awt/image/SinglePixelPackedSampleModel"))) {
        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);

    if ((*env)->IsInstanceOf(env, jraster,
         (*env)->FindClass(env, "sun/awt/image/IntegerComponentRaster"))){
        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,
            (*env)->FindClass(env, "sun/awt/image/ByteComponentRaster"))){
        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,
            (*env)->FindClass(env, "sun/awt/image/ShortComponentRaster"))){
        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,
            (*env)->FindClass(env, "sun/awt/image/BytePackedRaster"))){
        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) {
    int type = UNKNOWN_CM_TYPE;

    if ((*env)->IsInstanceOf(env, jcmodel,
                 (*env)->FindClass(env, "java/awt/image/IndexColorModel")))
    {
        type = INDEX_CM_TYPE;
    } else if ((*env)->IsInstanceOf(env, jcmodel,
                 (*env)->FindClass(env, "java/awt/image/PackedColorModel")))
    {
        if  ((*env)->IsInstanceOf(env, jcmodel,
                (*env)->FindClass(env, "java/awt/image/DirectColorModel"))) {
            type = DIRECT_CM_TYPE;
        }
        else {
            type = PACKED_CM_TYPE;
        }
    }
    else if ((*env)->IsInstanceOf(env, jcmodel,
                 (*env)->FindClass(env, "java/awt/image/ComponentColorModel")))
    {
        type = COMPONENT_CM_TYPE;
    }

    return 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;

    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;
    }

    cmP->nBits = NULL;
    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);

    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");
            defCM = (*env)->CallStaticObjectMethod(env, jcm,
                                                   g_CMgetRGBdefaultMID, NULL);
            s_jdefCM = (*env)->NewGlobalRef(env, defCM);
            if (defCM == NULL || s_jdefCM == NULL) {
                JNU_ThrowNullPointerException(env,
                                              "Unable to find default CM");
                return -1;
            }
        }
        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)
        {
            return -1;
        }

        for (i = 0; i < cmP->numComponents; i++) {
            if (cmP->nBits[i] != 8) {
                return -1;
            }
        }
    }

    /* 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) {
                return -1;
            }
            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;
}

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 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 if (rasterP->sppsm.offsets != NULL) {
            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;
}

/*
 * This routine will fill in a buffer of data for either 1 band or all
 * bands (if band == -1).
 */
#define MAX_TO_GRAB (10240)

int awt_getPixelByte(JNIEnv *env, int band, RasterS_t *rasterP,
                     unsigned char *bufferP) {
    int w = rasterP->width;
    int h = rasterP->height;
    int numBands = rasterP->numBands;
    int y;
    int i;
    int maxLines = (h < MAX_TO_GRAB/w ? h : MAX_TO_GRAB/w);
    jobject jsm;
    int off;
    jarray jdata = NULL;
    jobject jdatabuffer;
    int *dataP;
    int maxBytes = w;

    jsm = (*env)->GetObjectField(env, rasterP->jraster, g_RasterSampleModelID);
    jdatabuffer = (*env)->GetObjectField(env, rasterP->jraster,
                                         g_RasterDataBufferID);
    jdata = (*env)->NewIntArray(env, maxBytes*rasterP->numBands*maxLines);
    if (JNU_IsNull(env, jdata)) {
        JNU_ThrowOutOfMemoryError(env, "Out of Memory");
        return -1;
    }

    /* Here is the generic code */
    if (band >= 0) {
        int dOff;
        if (band >= numBands) {
            (*env)->DeleteLocalRef(env, jdata);
            JNU_ThrowInternalError(env, "Band out of range.");
            return -1;
        }
        off = 0;
        for (y=0; y < h; ) {
            (*env)->CallObjectMethod(env, jsm, g_SMGetPixelsMID,
                                     0, y, w,
                                     maxLines, jdata, jdatabuffer);
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            dOff = band;
            for (i=0; i < maxBytes; i++, dOff += numBands) {
                bufferP[off++] = (unsigned char) dataP[dOff];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);

            if (y+maxLines < h) {
                y += maxLines;
            }
            else {
                y++;
                maxBytes = w;
            }
        }
    }
    else {
        off = 0;
        maxBytes *= numBands;
        for (y=0; y < h; ) {
            (*env)->CallObjectMethod(env, jsm, g_SMGetPixelsMID,
                                     0, y, w,
                                     maxLines, jdata, jdatabuffer);
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            for (i=0; i < maxBytes; i++) {
                bufferP[off++] = (unsigned char) dataP[i];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);

            if (y+maxLines < h) {
                y += maxLines;
            }
            else {
                y++;
                maxBytes = w*numBands;
            }
        }

    }
    (*env)->DeleteLocalRef(env, jdata);

    return 0;
}
int awt_setPixelByte(JNIEnv *env, int band, RasterS_t *rasterP,
                     unsigned char *bufferP) {
    int w = rasterP->width;
    int h = rasterP->height;
    int numBands = rasterP->numBands;
    int y;
    int i;
    int maxLines = (h < MAX_TO_GRAB/w ? h : MAX_TO_GRAB/w);
    jobject jsm;
    int off;
    jarray jdata = NULL;
    jobject jdatabuffer;
    int *dataP;
    int maxBytes = w;

    jsm = (*env)->GetObjectField(env, rasterP->jraster, g_RasterSampleModelID);
    jdatabuffer = (*env)->GetObjectField(env, rasterP->jraster,
                                         g_RasterDataBufferID);
    /* Here is the generic code */
    jdata = (*env)->NewIntArray(env, maxBytes*rasterP->numBands*maxLines);
    if (JNU_IsNull(env, jdata)) {
        JNU_ThrowOutOfMemoryError(env, "Out of Memory");
        return -1;
    }
    if (band >= 0) {
        int dOff;
        if (band >= numBands) {
            (*env)->DeleteLocalRef(env, jdata);
            JNU_ThrowInternalError(env, "Band out of range.");
            return -1;
        }
        off = 0;
        for (y=0; y < h; y+=maxLines) {
            if (y+maxLines > h) {
                maxBytes = w*numBands;
                maxLines = h - y;
            }
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            dOff = band;
            for (i=0; i < maxBytes; i++, dOff += numBands) {
                dataP[dOff] = bufferP[off++];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);

            (*env)->CallVoidMethod(env, jsm, g_SMSetPixelsMID,
                                   0, y, w,
                                   maxLines, jdata, jdatabuffer);
        }
    }
    else {
        off = 0;
        maxBytes *= numBands;
        for (y=0; y < h; y+=maxLines) {
            if (y+maxLines > h) {
                maxBytes = w*numBands;
                maxLines = h - y;
            }
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            for (i=0; i < maxBytes; i++) {
                dataP[i] = bufferP[off++];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);

            (*env)->CallVoidMethod(env, jsm, g_SMSetPixelsMID,
                                     0, y, w,
                                     maxLines, jdata, jdatabuffer);
        }

    }

    (*env)->DeleteLocalRef(env, jdata);

    return 0;
}
int awt_getPixelShort(JNIEnv *env, int band, RasterS_t *rasterP,
                     unsigned short *bufferP) {
    int w = rasterP->width;
    int h = rasterP->height;
    int numBands = rasterP->numBands;
    int y;
    int i;
    int maxLines = (h < MAX_TO_GRAB/w ? h : MAX_TO_GRAB/w);
    jobject jsm;
    int off;
    jarray jdata = NULL;
    jobject jdatabuffer;
    int *dataP;
    int maxBytes = w*maxLines;

    jsm = (*env)->GetObjectField(env, rasterP->jraster, g_RasterSampleModelID);
    jdatabuffer = (*env)->GetObjectField(env, rasterP->jraster,
                                         g_RasterDataBufferID);
    jdata = (*env)->NewIntArray(env, maxBytes*rasterP->numBands*maxLines);
    if (JNU_IsNull(env, jdata)) {
        JNU_ThrowOutOfMemoryError(env, "Out of Memory");
        return -1;
    }
    /* Here is the generic code */
    if (band >= 0) {
        int dOff;
        if (band >= numBands) {
            (*env)->DeleteLocalRef(env, jdata);
            JNU_ThrowInternalError(env, "Band out of range.");
            return -1;
        }
        off = 0;
        for (y=0; y < h; y += maxLines) {
            if (y+maxLines > h) {
                maxBytes = w*numBands;
                maxLines = h - y;
            }
            (*env)->CallObjectMethod(env, jsm, g_SMGetPixelsMID,
                                     0, y, w,
                                     maxLines, jdata, jdatabuffer);
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }

            dOff = band;
            for (i=0; i < maxBytes; i++, dOff += numBands) {
                bufferP[off++] = (unsigned short) dataP[dOff];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);
        }
    }
    else {
        off = 0;
        maxBytes *= numBands;
        for (y=0; y < h; y+=maxLines) {
            if (y+maxLines > h) {
                maxBytes = w*numBands;
                maxLines = h - y;
            }
            (*env)->CallObjectMethod(env, jsm, g_SMGetPixelsMID,
                                     0, y, w,
                                     maxLines, jdata, jdatabuffer);
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            for (i=0; i < maxBytes; i++) {
                bufferP[off++] = (unsigned short) dataP[i];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);
        }

    }

    (*env)->DeleteLocalRef(env, jdata);
    return 0;
}
int awt_setPixelShort(JNIEnv *env, int band, RasterS_t *rasterP,
                      unsigned short *bufferP) {
    int w = rasterP->width;
    int h = rasterP->height;
    int numBands = rasterP->numBands;
    int y;
    int i;
    int maxLines = (h < MAX_TO_GRAB/w ? h : MAX_TO_GRAB/w);
    jobject jsm;
    int off;
    jarray jdata = NULL;
    jobject jdatabuffer;
    int *dataP;
    int maxBytes = w;

    jsm = (*env)->GetObjectField(env, rasterP->jraster, g_RasterSampleModelID);
    jdatabuffer = (*env)->GetObjectField(env, rasterP->jraster,
                                         g_RasterDataBufferID);
    if (band >= numBands) {
        JNU_ThrowInternalError(env, "Band out of range.");
        return -1;
    }
    /* Here is the generic code */
    jdata = (*env)->NewIntArray(env, maxBytes*rasterP->numBands*maxLines);
    if (JNU_IsNull(env, jdata)) {
        JNU_ThrowOutOfMemoryError(env, "Out of Memory");
        return -1;
    }
    if (band >= 0) {
        int dOff;
        off = 0;
        for (y=0; y < h; y+=maxLines) {
            if (y+maxLines > h) {
                maxBytes = w*numBands;
                maxLines = h - y;
            }
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            dOff = band;
            for (i=0; i < maxBytes; i++, dOff += numBands) {
                dataP[dOff] = bufferP[off++];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);

            (*env)->CallVoidMethod(env, jsm, g_SMSetPixelsMID,
                                   0, y, w,
                                   maxLines, jdata, jdatabuffer);
        }
    }
    else {
        off = 0;
        maxBytes *= numBands;
        for (y=0; y < h; y+=maxLines) {
            if (y+maxLines > h) {
                maxBytes = w*numBands;
                maxLines = h - y;
            }
            dataP = (int *) (*env)->GetPrimitiveArrayCritical(env, jdata,
                                                              NULL);
            if (dataP == NULL) {
                (*env)->DeleteLocalRef(env, jdata);
                return -1;
            }
            for (i=0; i < maxBytes; i++) {
                dataP[i] = bufferP[off++];
            }

            (*env)->ReleasePrimitiveArrayCritical(env, jdata, dataP,
                                                  JNI_ABORT);

            (*env)->CallVoidMethod(env, jsm, g_SMSetPixelsMID,
                                   0, y, w,
                                   maxLines, jdata, jdatabuffer);
        }

    }

    (*env)->DeleteLocalRef(env, jdata);
    return 0;
}