/*
 * Copyright (c) 1997, 2016, 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 <string.h>
#include "sun_awt_image_ImagingLib.h"
#include "java_awt_Transparency.h"
#include "java_awt_image_AffineTransformOp.h"
#include "java_awt_image_BufferedImage.h"
#include "java_awt_color_ColorSpace.h"
#include "java_awt_image_ConvolveOp.h"
#include "sun_awt_image_IntegerComponentRaster.h"
#include "awt_ImagingLib.h"
#include "awt_parseImage.h"
#include "imageInitIDs.h"
#include <jni.h>
#include <jni_util.h>
#include <assert.h>
#include "awt_Mlib.h"
#include "gdefs.h"
#include "safe_alloc.h"
#include "safe_math.h"

/***************************************************************************
 *                               Definitions                               *
 ***************************************************************************/
#define jio_fprintf fprintf

#ifndef TRUE
#define TRUE 1
#endif /* TRUE */

#ifndef FALSE
#define FALSE 0
#endif /* FALSE */

#define TYPE_CUSTOM         java_awt_image_BufferedImage_TYPE_CUSTOM
#define TYPE_INT_RGB        java_awt_image_BufferedImage_TYPE_INT_RGB
#define TYPE_INT_ARGB       java_awt_image_BufferedImage_TYPE_INT_ARGB
#define TYPE_INT_ARGB_PRE   java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE
#define TYPE_INT_BGR        java_awt_image_BufferedImage_TYPE_INT_BGR
#define TYPE_4BYTE_ABGR     java_awt_image_BufferedImage_TYPE_4BYTE_ABGR
#define TYPE_4BYTE_ABGR_PRE java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE

/* (alpha*color)>>nbits + alpha>>(nbits-1) */
#define BLEND(color, alpha, alphaNbits) \
    ((((alpha)*(color))>>(alphaNbits)) + ((alpha) >> ((alphaNbits)-1)))

    /* ((color - (alpha>>(nBits-1)))<<nBits)/alpha */
#define UNBLEND(color, alpha, alphaNbits) \
    ((((color)-((alpha)>>((alphaNbits)-1)))<<(alphaNbits))/(alpha))

/* Enumeration of all of the mlib functions used */
typedef enum {
    MLIB_CONVMxN,
    MLIB_AFFINE,
    MLIB_LOOKUP,
    MLIB_CONVKERNCVT
} mlibTypeE_t;

typedef struct {
    int dataType;           /* One of BYTE_DATA_TYPE, SHORT_DATA_TYPE, */
    int needToCopy;
    int cvtSrcToDefault;    /* If TRUE, convert the src to def CM (pre?) */
    int allocDefaultDst;    /* If TRUE, alloc def CM dst buffer */
    int cvtToDst;           /* If TRUE, convert dst buffer to Dst CM */
    int addAlpha;
} mlibHintS_t;

/***************************************************************************
 *                     Static Variables/Structures                         *
 ***************************************************************************/

static mlibSysFnS_t sMlibSysFns = {
    NULL, // placeholder for j2d_mlib_ImageCreate
    NULL, // placeholder for j2d_mlib_ImageCreateStruct
    NULL, // placeholder for j2d_mlib_ImageDelete
};

static mlibFnS_t sMlibFns[] = {
    {NULL, "j2d_mlib_ImageConvMxN"},
    {NULL, "j2d_mlib_ImageAffine"},
    {NULL, "j2d_mlib_ImageLookUp"},
    {NULL, "j2d_mlib_ImageConvKernelConvert"},
    {NULL, NULL},
};

static int s_timeIt = 0;
static int s_printIt = 0;
static int s_startOff = 0;
static int s_nomlib = 0;

/***************************************************************************
 *                          Static Function Prototypes                     *
 ***************************************************************************/

static int
allocateArray(JNIEnv *env, BufImageS_t *imageP,
              mlib_image **mlibImagePP, void **dataPP, int isSrc,
              int cvtToDefault, int addAlpha);
static int
allocateRasterArray(JNIEnv *env, RasterS_t *rasterP,
                    mlib_image **mlibImagePP, void **dataPP, int isSrc);

static void
freeArray(JNIEnv *env, BufImageS_t *srcimageP, mlib_image *srcmlibImP,
          void *srcdataP, BufImageS_t *dstimageP, mlib_image *dstmlibImP,
          void *dstdataP);
static void
freeDataArray(JNIEnv *env, jobject srcJdata, mlib_image *srcmlibImP,
          void *srcdataP, jobject dstJdata, mlib_image *dstmlibImP,
          void *dstdataP);

static int
storeImageArray(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP,
                mlib_image *mlibImP);

static int
storeRasterArray(JNIEnv *env, RasterS_t *srcP, RasterS_t *dstP,
                mlib_image *mlibImP);

static int
storeICMarray(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP,
              mlib_image *mlibImP);

static int
colorMatch(int r, int g, int b, int a, unsigned char *argb, int numColors);

static int
setImageHints(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP,
              int expandICM, int useAlpha,
              int premultiply, mlibHintS_t *hintP);


static int expandICM(JNIEnv *env, BufImageS_t *imageP, unsigned int *mDataP);
static int expandPackedBCR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *outDataP);
static int expandPackedSCR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *outDataP);
static int expandPackedICR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *outDataP);
static int expandPackedBCRdefault(JNIEnv *env, RasterS_t *rasterP,
                                  int component, unsigned char *outDataP,
                                  int forceAlpha);
static int expandPackedSCRdefault(JNIEnv *env, RasterS_t *rasterP,
                                  int component, unsigned char *outDataP,
                                  int forceAlpha);
static int expandPackedICRdefault(JNIEnv *env, RasterS_t *rasterP,
                                  int component, unsigned char *outDataP,
                                  int forceAlpha);
static int setPackedBCR(JNIEnv *env, RasterS_t *rasterP, int component,
                        unsigned char *outDataP);
static int setPackedSCR(JNIEnv *env, RasterS_t *rasterP, int component,
                        unsigned char *outDataP);
static int setPackedICR(JNIEnv *env, RasterS_t *rasterP, int component,
                        unsigned char *outDataP);
static int setPackedBCRdefault(JNIEnv *env, RasterS_t *rasterP,
                               int component, unsigned char *outDataP,
                               int supportsAlpha);
static int setPackedSCRdefault(JNIEnv *env, RasterS_t *rasterP,
                               int component, unsigned char *outDataP,
                               int supportsAlpha);
static int setPackedICRdefault(JNIEnv *env, RasterS_t *rasterP,
                               int component, unsigned char *outDataP,
                               int supportsAlpha);

mlib_start_timer start_timer = NULL;
mlib_stop_timer stop_timer = NULL;

/***************************************************************************
 *                          Debugging Definitions                          *
 ***************************************************************************/
#ifdef DEBUG

static void
printMedialibError(int status) {
    switch(status) {
    case MLIB_FAILURE:
        jio_fprintf(stderr, "failure\n");
        break;
    case MLIB_NULLPOINTER:
        jio_fprintf(stderr, "null pointer\n");
        break;
    case MLIB_OUTOFRANGE:
        jio_fprintf (stderr, "out of range\n");
        break;
    default:
        jio_fprintf (stderr, "medialib error\n");
        break;
    }
}
#else /* ! DEBUG */
#  define printMedialibError(x)

#endif /* ! DEBUG */

static int
getMlibEdgeHint(jint edgeHint) {
    switch (edgeHint) {
    case java_awt_image_ConvolveOp_EDGE_NO_OP:
        return MLIB_EDGE_DST_COPY_SRC;
    case java_awt_image_ConvolveOp_EDGE_ZERO_FILL:
    default:
        return MLIB_EDGE_DST_FILL_ZERO;
    }
}

/*
 * We have to make sure that awt_setPixels can be safely applied to the given pair of
 * raster and mlib image.
 *
 * In particular, make sure that
 *  - dimension is the same
 *  - number of channels in mlib image corresponds to the number of bands in the raster
 *  - sample size in image and raster are the same.
 *
 * Returns:
 *  -1 to indicate failure,
 *   1 to indicate success
 */
static int setPixelsFormMlibImage(JNIEnv *env, RasterS_t *rasterP, mlib_image* img) {
    if (rasterP->width != img->width || rasterP->height != img->height) {
        /* dimension does not match */
        return -1;
    }

    if (rasterP->numBands != img->channels) {
        /* number of bands does not match */
        return -1;
    }

    switch (rasterP->dataType) {
    case BYTE_DATA_TYPE:
        if (img->type != MLIB_BYTE) {
            return -1;
        }
        break;
    case SHORT_DATA_TYPE:
        if (img->type != MLIB_SHORT && img->type != MLIB_USHORT) {
            return -1;
        }
        break;
    default:
        /* awt_setPixels does not support such rasters */
        return -1;
    }

    return awt_setPixels(env, rasterP, mlib_ImageGetData(img));
}

/***************************************************************************
 *                          External Functions                             *
 ***************************************************************************/
JNIEXPORT jint JNICALL
Java_sun_awt_image_ImagingLib_convolveBI(JNIEnv *env, jobject this,
                                         jobject jsrc, jobject jdst,
                                         jobject jkernel, jint edgeHint)
{
    void *sdata, *ddata;
    mlib_image *src;
    mlib_image *dst;
    int i, scale;
    mlib_d64 *dkern;
    mlib_s32 *kdata;
    int klen;
    float kmax;
    mlib_s32 cmask;
    mlib_status status;
    int retStatus = 1;
    float *kern;
    BufImageS_t *srcImageP, *dstImageP;
    jobject jdata;
    int kwidth;
    int kheight;
    int w, h;
    int x, y;
    mlibHintS_t hint;
    int nbands;

    /* This function requires a lot of local refs ??? Is 64 enough ??? */
    if ((*env)->EnsureLocalCapacity(env, 64) < 0)
        return 0;

    if (s_nomlib) return 0;
    if (s_timeIt)     (*start_timer)(3600);

    kwidth  = (*env)->GetIntField(env, jkernel, g_KernelWidthID);
    kheight = (*env)->GetIntField(env, jkernel, g_KernelHeightID);
    jdata = (*env)->GetObjectField(env, jkernel, g_KernelDataID);
    klen  = (*env)->GetArrayLength(env, jdata);
    kern  = (float *) (*env)->GetPrimitiveArrayCritical(env, jdata, NULL);
    if (kern == NULL) {
        /* out of memory exception already thrown */
        return 0;
    }

    if ((kwidth&0x1) == 0) {
        /* Kernel has even width */
        w = kwidth+1;
    }
    else {
        w = kwidth;
    }
    if ((kheight&0x1) == 0) {
        /* Kernel has even height */
        h = kheight+1;
    }
    else {
        h = kheight;
    }

    dkern = NULL;
    if (SAFE_TO_ALLOC_3(w, h, sizeof(mlib_d64))) {
        dkern = (mlib_d64 *)calloc(1, w * h * sizeof(mlib_d64));
    }
    if (dkern == NULL) {
        (*env)->ReleasePrimitiveArrayCritical(env, jdata, kern, JNI_ABORT);
        return 0;
    }

    /* Need to flip and find max value of the kernel.
     * Also, save the kernel values as mlib_d64 values.
     * The flip is to operate correctly with medialib,
     * which doesn't do the mathemetically correct thing,
     * i.e. it doesn't rotate the kernel by 180 degrees.
     * REMIND: This should perhaps be done at the Java
     * level by ConvolveOp.
     * REMIND: Should the max test be looking at absolute
     * values?
     * REMIND: What if klen != kheight * kwidth?
     */
    kmax = kern[klen-1];
    i = klen-1;
    for (y=0; y < kheight; y++) {
        for (x=0; x < kwidth; x++, i--) {
            dkern[y*w+x] = (mlib_d64) kern[i];
            if (kern[i] > kmax) {
                kmax = kern[i];
            }
        }
    }

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

    if (kmax > 1<<16) {
        /* We can only handle 16 bit max */
        free(dkern);
        return 0;
    }


    /* Parse the source image */
    if (awt_parseImage(env, jsrc, &srcImageP, FALSE) <= 0) {
        /* Can't handle any custom images */
        free(dkern);
        return 0;
    }

    /* Parse the destination image */
    if (awt_parseImage(env, jdst, &dstImageP, FALSE) <= 0) {
        /* Can't handle any custom images */
        awt_freeParsedImage(srcImageP, TRUE);
        free(dkern);
        return 0;
    }

    nbands = setImageHints(env, srcImageP, dstImageP, TRUE, TRUE,
                        FALSE, &hint);
    if (nbands < 1) {
        /* Can't handle any custom images */
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        free(dkern);
        return 0;
    }
    /* Allocate the arrays */
    if (allocateArray(env, srcImageP, &src, &sdata, TRUE,
                      hint.cvtSrcToDefault, hint.addAlpha) < 0) {
        /* Must be some problem */
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        free(dkern);
        return 0;
    }
    if (allocateArray(env, dstImageP, &dst, &ddata, FALSE,
                      hint.cvtToDst, FALSE) < 0) {
        /* Must be some problem */
        freeArray(env, srcImageP, src, sdata, NULL, NULL, NULL);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        free(dkern);
        return 0;
    }

    kdata = NULL;
    if (SAFE_TO_ALLOC_3(w, h, sizeof(mlib_s32))) {
        kdata = (mlib_s32 *)malloc(w * h * sizeof(mlib_s32));
    }
    if (kdata == NULL) {
        freeArray(env, srcImageP, src, sdata, dstImageP, dst, ddata);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        free(dkern);
        return 0;
    }

    if ((*sMlibFns[MLIB_CONVKERNCVT].fptr)(kdata, &scale, dkern, w, h,
                                    mlib_ImageGetType(src)) != MLIB_SUCCESS) {
        freeArray(env, srcImageP, src, sdata, dstImageP, dst, ddata);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        free(dkern);
        free(kdata);
        return 0;
    }

    if (s_printIt) {
        fprintf(stderr, "Orig Kernel(len=%d):\n",klen);
        for (y=kheight-1; y >= 0; y--) {
            for (x=kwidth-1; x >= 0; x--) {
                fprintf(stderr, "%g ", dkern[y*w+x]);
            }
            fprintf(stderr, "\n");
        }
        fprintf(stderr, "New Kernel(scale=%d):\n", scale);
        for (y=kheight-1; y >= 0; y--) {
            for (x=kwidth-1; x >= 0; x--) {
                fprintf(stderr, "%d ", kdata[y*w+x]);
            }
            fprintf(stderr, "\n");
        }
    }

    cmask = (1<<src->channels)-1;
    status = (*sMlibFns[MLIB_CONVMxN].fptr)(dst, src, kdata, w, h,
                               (w-1)/2, (h-1)/2, scale, cmask,
                               getMlibEdgeHint(edgeHint));

    if (status != MLIB_SUCCESS) {
        printMedialibError(status);
        retStatus = 0;
    }

    if (s_printIt) {
        unsigned int *dP;
        if (s_startOff != 0) {
            printf("Starting at %d\n", s_startOff);
        }
        if (sdata == NULL) {
            dP = (unsigned int *) mlib_ImageGetData(src);
        }
        else {
            dP = (unsigned int *) sdata;
        }
        printf("src is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[s_startOff+i]);
        }
        printf("\n");
        if (ddata == NULL) {
            dP = (unsigned int *)mlib_ImageGetData(dst);
        }
        else {
            dP = (unsigned int *) ddata;
        }
        printf("dst is \n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[s_startOff+i]);
        }
        printf("\n");
    }

    /* Means that we couldn't write directly into the destination buffer */
    if (ddata == NULL) {

        /* Need to store it back into the array */
        if (storeImageArray(env, srcImageP, dstImageP, dst) < 0) {
            /* Error */
            retStatus = 0;
        }
    }

    /* Release the pinned memory */
    freeArray(env, srcImageP, src, sdata, dstImageP, dst, ddata);
    awt_freeParsedImage(srcImageP, TRUE);
    awt_freeParsedImage(dstImageP, TRUE);
    free(dkern);
    free(kdata);

    if (s_timeIt) (*stop_timer)(3600, 1);

    return retStatus;
}

JNIEXPORT jint JNICALL
Java_sun_awt_image_ImagingLib_convolveRaster(JNIEnv *env, jobject this,
                                             jobject jsrc, jobject jdst,
                                             jobject jkernel, jint edgeHint)
{
    mlib_image *src;
    mlib_image *dst;
    int i, scale;
    mlib_d64 *dkern;
    mlib_s32 *kdata;
    int klen;
    float kmax;
    int retStatus = 1;
    mlib_status status;
    mlib_s32 cmask;
    void *sdata;
    void *ddata;
    RasterS_t *srcRasterP;
    RasterS_t *dstRasterP;
    int kwidth;
    int kheight;
    int w, h;
    int x, y;
    jobject jdata;
    float *kern;

    /* This function requires a lot of local refs ??? Is 64 enough ??? */
    if ((*env)->EnsureLocalCapacity(env, 64) < 0)
        return 0;

    if (s_nomlib) return 0;
    if (s_timeIt)     (*start_timer)(3600);

    kwidth  = (*env)->GetIntField(env, jkernel, g_KernelWidthID);
    kheight = (*env)->GetIntField(env, jkernel, g_KernelHeightID);
    jdata = (*env)->GetObjectField(env, jkernel, g_KernelDataID);
    klen  = (*env)->GetArrayLength(env, jdata);
    kern  = (float *) (*env)->GetPrimitiveArrayCritical(env, jdata, NULL);
    if (kern == NULL) {
        /* out of memory exception already thrown */
        return 0;
    }

    if ((kwidth&0x1) == 0) {
        /* Kernel has even width */
        w = kwidth+1;
    }
    else {
        w = kwidth;
    }
    if ((kheight&0x1) == 0) {
        /* Kernel has even height */
        h = kheight+1;
    }
    else {
        h = kheight;
    }

    dkern = NULL;
    if (SAFE_TO_ALLOC_3(w, h, sizeof(mlib_d64))) {
        dkern = (mlib_d64 *)calloc(1, w * h * sizeof(mlib_d64));
    }
    if (dkern == NULL) {
        (*env)->ReleasePrimitiveArrayCritical(env, jdata, kern, JNI_ABORT);
        return 0;
    }

    /* Need to flip and find max value of the kernel.
     * Also, save the kernel values as mlib_d64 values.
     * The flip is to operate correctly with medialib,
     * which doesn't do the mathemetically correct thing,
     * i.e. it doesn't rotate the kernel by 180 degrees.
     * REMIND: This should perhaps be done at the Java
     * level by ConvolveOp.
     * REMIND: Should the max test be looking at absolute
     * values?
     * REMIND: What if klen != kheight * kwidth?
     */
    kmax = kern[klen-1];
    i = klen-1;
    for (y=0; y < kheight; y++) {
        for (x=0; x < kwidth; x++, i--) {
            dkern[y*w+x] = (mlib_d64) kern[i];
            if (kern[i] > kmax) {
                kmax = kern[i];
            }
        }
    }

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

    if (kmax > 1<<16) {
        /* We can only handle 16 bit max */
        free(dkern);
        return 0;
    }

    /* Parse the source image */
    if ((srcRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
        JNU_ThrowOutOfMemoryError(env, "Out of memory");
        free(dkern);
        return -1;
    }

    if ((dstRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
        JNU_ThrowOutOfMemoryError(env, "Out of memory");
        free(srcRasterP);
        free(dkern);
        return -1;
    }

    /* Parse the source raster */
    if (awt_parseRaster(env, jsrc, srcRasterP) <= 0) {
        /* Can't handle any custom rasters */
        free(srcRasterP);
        free(dstRasterP);
        free(dkern);
        return 0;
    }

    /* Parse the destination raster */
    if (awt_parseRaster(env, jdst, dstRasterP) <= 0) {
        /* Can't handle any custom images */
        awt_freeParsedRaster(srcRasterP, TRUE);
        free(dstRasterP);
        free(dkern);
        return 0;
    }

    /* Allocate the arrays */
    if (allocateRasterArray(env, srcRasterP, &src, &sdata, TRUE) < 0) {
        /* Must be some problem */
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        free(dkern);
        return 0;
    }
    if (allocateRasterArray(env, dstRasterP, &dst, &ddata, FALSE) < 0) {
        /* Must be some problem */
        freeDataArray(env, srcRasterP->jdata, src, sdata, NULL, NULL, NULL);
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        free(dkern);
        return 0;
    }

    kdata = NULL;
    if (SAFE_TO_ALLOC_3(w, h, sizeof(mlib_s32))) {
        kdata = (mlib_s32 *)malloc(w * h * sizeof(mlib_s32));
    }
    if (kdata == NULL) {
        freeDataArray(env, srcRasterP->jdata, src, sdata,
                      dstRasterP->jdata, dst, ddata);
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        free(dkern);
        return 0;
    }

    if ((*sMlibFns[MLIB_CONVKERNCVT].fptr)(kdata, &scale, dkern, w, h,
                                    mlib_ImageGetType(src)) != MLIB_SUCCESS) {
        freeDataArray(env, srcRasterP->jdata, src, sdata,
                      dstRasterP->jdata, dst, ddata);
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        free(dkern);
        free(kdata);
        return 0;
    }

    if (s_printIt) {
        fprintf(stderr, "Orig Kernel(len=%d):\n",klen);
        for (y=kheight-1; y >= 0; y--) {
            for (x=kwidth-1; x >= 0; x--) {
                fprintf(stderr, "%g ", dkern[y*w+x]);
            }
            fprintf(stderr, "\n");
        }
        fprintf(stderr, "New Kernel(scale=%d):\n", scale);
        for (y=kheight-1; y >= 0; y--) {
            for (x=kwidth-1; x >= 0; x--) {
                fprintf(stderr, "%d ", kdata[y*w+x]);
            }
            fprintf(stderr, "\n");
        }
    }

    cmask = (1<<src->channels)-1;
    status = (*sMlibFns[MLIB_CONVMxN].fptr)(dst, src, kdata, w, h,
                               (w-1)/2, (h-1)/2, scale, cmask,
                               getMlibEdgeHint(edgeHint));

    if (status != MLIB_SUCCESS) {
        printMedialibError(status);
        retStatus = 0;
    }

    if (s_printIt) {
        unsigned int *dP;
        if (s_startOff != 0) {
            printf("Starting at %d\n", s_startOff);
        }
        if (sdata == NULL) {
            dP = (unsigned int *) mlib_ImageGetData(src);
        }
        else {
            dP = (unsigned int *) sdata;
        }
        printf("src is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[s_startOff+i]);
        }
        printf("\n");
        if (ddata == NULL) {
            dP = (unsigned int *)mlib_ImageGetData(dst);
        }
        else {
            dP = (unsigned int *) ddata;
        }
        printf("dst is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[s_startOff+i]);
        }
        printf("\n");
    }

    /* Means that we couldn't write directly into the destination buffer */
    if (ddata == NULL) {
        if (storeRasterArray(env, srcRasterP, dstRasterP, dst) < 0) {
            retStatus = setPixelsFormMlibImage(env, dstRasterP, dst);
        }
    }

    /* Release the pinned memory */
    freeDataArray(env, srcRasterP->jdata, src, sdata,
                  dstRasterP->jdata, dst, ddata);
    awt_freeParsedRaster(srcRasterP, TRUE);
    awt_freeParsedRaster(dstRasterP, TRUE);
    free(dkern);
    free(kdata);

    if (s_timeIt) (*stop_timer)(3600,1);

    return retStatus;
}


JNIEXPORT jint JNICALL
Java_sun_awt_image_ImagingLib_transformBI(JNIEnv *env, jobject this,
                                          jobject jsrc,
                                          jobject jdst,
                                          jdoubleArray jmatrix,
                                          jint interpType)
{
    mlib_image *src;
    mlib_image *dst;
    int i;
    int j = 0;
    int retStatus = 1;
    mlib_status status;
    double *matrix;
    mlib_d64 mtx[6];
    void *sdata;
    void *ddata;
    BufImageS_t *srcImageP;
    BufImageS_t *dstImageP;
    mlib_filter filter;
    mlibHintS_t hint;
    unsigned int *dP;
    int useIndexed;
    int nbands;

    /* This function requires a lot of local refs ??? Is 64 enough ??? */
    if ((*env)->EnsureLocalCapacity(env, 64) < 0)
        return 0;

    if (s_nomlib) return 0;
    if (s_timeIt) {
        (*start_timer)(3600);
    }

    switch(interpType) {
    case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
        filter = MLIB_BILINEAR;
        break;
    case java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR:
        filter = MLIB_NEAREST;
        break;
    case java_awt_image_AffineTransformOp_TYPE_BICUBIC:
        filter = MLIB_BICUBIC;
        break;
    default:
        JNU_ThrowInternalError(env, "Unknown interpolation type");
        return -1;
    }

    if ((*env)->GetArrayLength(env, jmatrix) < 6) {
        /*
         * Very unlikely, however we should check for this:
         * if given matrix array is too short, we can't handle it
         */
        return 0;
    }

    matrix = (*env)->GetPrimitiveArrayCritical(env, jmatrix, NULL);
    if (matrix == NULL) {
        /* out of memory error already thrown */
        return 0;
    }

    /* Check for invalid double value in transformation matrix */
    for (j = 0; j < 6; j++) {

        if (!(IS_FINITE(matrix[j]))) {
            (*env)->ReleasePrimitiveArrayCritical(env, jmatrix, matrix, JNI_ABORT);
            return 0;
        }
    }

    if (s_printIt) {
        printf("matrix is %g %g %g %g %g %g\n", matrix[0], matrix[1],
               matrix[2], matrix[3], matrix[4], matrix[5]);
    }

    mtx[0] = matrix[0];
    mtx[1] = matrix[2];
    mtx[2] = matrix[4];
    mtx[3] = matrix[1];
    mtx[4] = matrix[3];
    mtx[5] = matrix[5];

    (*env)->ReleasePrimitiveArrayCritical(env, jmatrix, matrix, JNI_ABORT);

    /* Parse the source image */
    if (awt_parseImage(env, jsrc, &srcImageP, FALSE) <= 0) {
        /* Can't handle any custom images */
        return 0;
    }

    /* Parse the destination image */
    if (awt_parseImage(env, jdst, &dstImageP, FALSE) <= 0) {
        /* Can't handle any custom images */
        awt_freeParsedImage(srcImageP, TRUE);
        return 0;
    }

    /* REMIND!!  Can't assume that it is the same LUT!! */
    /* Fix 4213160, 4184283 */
    useIndexed = (srcImageP->cmodel.cmType == INDEX_CM_TYPE &&
                  dstImageP->cmodel.cmType == INDEX_CM_TYPE &&
                  srcImageP->raster.rasterType == dstImageP->raster.rasterType &&
                  srcImageP->raster.rasterType == COMPONENT_RASTER_TYPE);

    nbands = setImageHints(env, srcImageP, dstImageP, !useIndexed, TRUE,
                        FALSE, &hint);
    if (nbands < 1) {
        /* Can't handle any custom images */
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        return 0;
    }

    /* Allocate the arrays */
    if (allocateArray(env, srcImageP, &src, &sdata, TRUE,
                      hint.cvtSrcToDefault, hint.addAlpha) < 0) {
        /* Must be some problem */
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        return 0;
    }
    if (allocateArray(env, dstImageP, &dst, &ddata, FALSE,
                      hint.cvtToDst, FALSE) < 0) {
        /* Must be some problem */
        freeArray(env, srcImageP, src, sdata, NULL, NULL, NULL);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        return 0;
    }
#if 0
fprintf(stderr,"Src----------------\n");
fprintf(stderr,"Type : %d\n",src->type);
fprintf(stderr,"Channels: %d\n",src->channels);
fprintf(stderr,"Width   : %d\n",src->width);
fprintf(stderr,"Height  : %d\n",src->height);
fprintf(stderr,"Stride  : %d\n",src->stride);
fprintf(stderr,"Flags   : %d\n",src->flags);

fprintf(stderr,"Dst----------------\n");
fprintf(stderr,"Type : %d\n",dst->type);
fprintf(stderr,"Channels: %d\n",dst->channels);
fprintf(stderr,"Width   : %d\n",dst->width);
fprintf(stderr,"Height  : %d\n",dst->height);
fprintf(stderr,"Stride  : %d\n",dst->stride);
fprintf(stderr,"Flags   : %d\n",dst->flags);
#endif

    if (dstImageP->cmodel.cmType == INDEX_CM_TYPE) {
        /* Need to clear the destination to the transparent pixel */
        unsigned char *cP = (unsigned char *)mlib_ImageGetData(dst);

        memset(cP, dstImageP->cmodel.transIdx,
               mlib_ImageGetWidth(dst)*mlib_ImageGetHeight(dst));
    }
    /* Perform the transformation */
    if ((status = (*sMlibFns[MLIB_AFFINE].fptr)(dst, src, mtx, filter,
                                  MLIB_EDGE_SRC_EXTEND) != MLIB_SUCCESS))
    {
        printMedialibError(status);
        freeArray(env, srcImageP, src, sdata, dstImageP, dst, ddata);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);

        return 0;
    }

    if (s_printIt) {
        if (sdata == NULL) {
            dP = (unsigned int *) mlib_ImageGetData(src);
        }
        else {
            dP = (unsigned int *) sdata;
        }
        printf("src is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[i]);
        }
        printf("\n");
        if (ddata == NULL) {
            dP = (unsigned int *)mlib_ImageGetData(dst);
        }
        else {
            dP = (unsigned int *) ddata;
        }
        printf("dst is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[i]);
        }
        printf("\n");
    }

    /* Means that we couldn't write directly into the destination buffer */
    if (ddata == NULL) {
        freeDataArray(env, srcImageP->raster.jdata, src, sdata,
                      NULL, NULL, NULL);
        /* Need to store it back into the array */
        if (storeImageArray(env, srcImageP, dstImageP, dst) < 0) {
            /* Error */
            retStatus = 0;
        }
        freeDataArray(env, NULL, NULL, NULL, dstImageP->raster.jdata,
                      dst, ddata);
    }
    else {
        /* Release the pinned memory */
        freeArray(env, srcImageP, src, sdata, dstImageP, dst, ddata);
    }

    awt_freeParsedImage(srcImageP, TRUE);
    awt_freeParsedImage(dstImageP, TRUE);

    if (s_timeIt) (*stop_timer)(3600,1);

    return retStatus;
}

JNIEXPORT jint JNICALL
Java_sun_awt_image_ImagingLib_transformRaster(JNIEnv *env, jobject this,
                                              jobject jsrc,
                                              jobject jdst,
                                              jdoubleArray jmatrix,
                                              jint interpType)
{
    mlib_image *src;
    mlib_image *dst;
    int i;
    int j = 0;
    int retStatus = 1;
    mlib_status status;
    double *matrix;
    mlib_d64 mtx[6];
    void *sdata;
    void *ddata;
    RasterS_t *srcRasterP;
    RasterS_t *dstRasterP;
    mlib_filter filter;
    unsigned int *dP;

    /* This function requires a lot of local refs ??? Is 64 enough ??? */
    if ((*env)->EnsureLocalCapacity(env, 64) < 0)
        return 0;

    if (s_nomlib) return 0;
    if (s_timeIt) {
        (*start_timer)(3600);
    }

    switch(interpType) {
    case java_awt_image_AffineTransformOp_TYPE_BILINEAR:
        filter = MLIB_BILINEAR;
        break;
    case java_awt_image_AffineTransformOp_TYPE_NEAREST_NEIGHBOR:
        filter = MLIB_NEAREST;
        break;
    case java_awt_image_AffineTransformOp_TYPE_BICUBIC:
        filter = MLIB_BICUBIC;
        break;
    default:
        JNU_ThrowInternalError(env, "Unknown interpolation type");
        return -1;
    }

    if ((srcRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
        JNU_ThrowOutOfMemoryError(env, "Out of memory");
        return -1;
    }

    if ((dstRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
        JNU_ThrowOutOfMemoryError(env, "Out of memory");
        free(srcRasterP);
        return -1;
    }

    if ((*env)->GetArrayLength(env, jmatrix) < 6) {
        /*
         * Very unlikely, however we should check for this:
         * if given matrix array is too short, we can't handle it.
         */
        free(srcRasterP);
        free(dstRasterP);
        return 0;
    }

    matrix = (*env)->GetPrimitiveArrayCritical(env, jmatrix, NULL);
    if (matrix == NULL) {
        /* out of memory error already thrown */
        free(srcRasterP);
        free(dstRasterP);
        return 0;
    }

    /* Check for invalid double value in transformation matrix */
    for (j = 0; j < 6; j++) {

        if (!(IS_FINITE(matrix[j]))) {
            (*env)->ReleasePrimitiveArrayCritical(env, jmatrix, matrix, JNI_ABORT);
            free(srcRasterP);
            free(dstRasterP);
            return 0;
        }
    }

    if (s_printIt) {
        printf("matrix is %g %g %g %g %g %g\n", matrix[0], matrix[1],
               matrix[2], matrix[3], matrix[4], matrix[5]);
    }

    mtx[0] = matrix[0];
    mtx[1] = matrix[2];
    mtx[2] = matrix[4];
    mtx[3] = matrix[1];
    mtx[4] = matrix[3];
    mtx[5] = matrix[5];

    (*env)->ReleasePrimitiveArrayCritical(env, jmatrix, matrix, JNI_ABORT);

    /* Parse the source raster */
    if (awt_parseRaster(env, jsrc, srcRasterP) <= 0) {
        /* Can't handle any custom rasters */
        free(srcRasterP);
        free(dstRasterP);
        return 0;
    }

    /* Parse the destination raster */
    if (awt_parseRaster(env, jdst, dstRasterP) <= 0) {
        /* Can't handle any custom images */
        awt_freeParsedRaster(srcRasterP, TRUE);
        free(dstRasterP);
        return 0;
    }

    /* Allocate the arrays */
    if (allocateRasterArray(env, srcRasterP, &src, &sdata, TRUE) < 0) {
        /* Must be some problem */
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        return 0;
    }
    if (allocateRasterArray(env, dstRasterP, &dst, &ddata, FALSE) < 0) {
        /* Must be some problem */
        freeDataArray(env, srcRasterP->jdata, src, sdata, NULL, NULL, NULL);
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        return 0;
    }

#if 0
fprintf(stderr,"Src----------------\n");
fprintf(stderr,"Type : %d\n",src->type);
fprintf(stderr,"Channels: %d\n",src->channels);
fprintf(stderr,"Width   : %d\n",src->width);
fprintf(stderr,"Height  : %d\n",src->height);
fprintf(stderr,"Stride  : %d\n",src->stride);
fprintf(stderr,"Flags   : %d\n",src->flags);

fprintf(stderr,"Dst----------------\n");
fprintf(stderr,"Type : %d\n",dst->type);
fprintf(stderr,"Channels: %d\n",dst->channels);
fprintf(stderr,"Width   : %d\n",dst->width);
fprintf(stderr,"Height  : %d\n",dst->height);
fprintf(stderr,"Stride  : %d\n",dst->stride);
fprintf(stderr,"Flags   : %d\n",dst->flags);
#endif

    {
        unsigned char *cP = (unsigned char *)mlib_ImageGetData(dst);

        memset(cP, 0, mlib_ImageGetWidth(dst)*mlib_ImageGetHeight(dst));
    }

    /* Perform the transformation */
    if ((status = (*sMlibFns[MLIB_AFFINE].fptr)(dst, src, mtx, filter,
                                  MLIB_EDGE_SRC_EXTEND) != MLIB_SUCCESS))
    {
        printMedialibError(status);
        /* REMIND: Free the regions */
        return 0;
    }

    if (s_printIt) {
        if (sdata == NULL) {
            dP = (unsigned int *) mlib_ImageGetData(src);
        }
        else {
            dP = (unsigned int *) sdata;
        }
        printf("src is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[i]);
        }
        printf("\n");
        if (ddata == NULL) {
            dP = (unsigned int *)mlib_ImageGetData(dst);
        }
        else {
            dP = (unsigned int *) ddata;
        }
        printf("dst is\n");
        for (i=0; i < 20; i++) {
            printf("%x ",dP[i]);
        }
        printf("\n");
    }

    /* Means that we couldn't write directly into the destination buffer */
    if (ddata == NULL) {
        /* Need to store it back into the array */
        if (storeRasterArray(env, srcRasterP, dstRasterP, dst) < 0) {
            (*env)->ExceptionClear(env); // Could not store the array, try another way
            retStatus = setPixelsFormMlibImage(env, dstRasterP, dst);
        }
    }

    /* Release the pinned memory */
    freeDataArray(env, srcRasterP->jdata, src, sdata,
                  dstRasterP->jdata, dst, ddata);

    awt_freeParsedRaster(srcRasterP, TRUE);
    awt_freeParsedRaster(dstRasterP, TRUE);

    if (s_timeIt) (*stop_timer)(3600,1);

    return retStatus;
}

typedef struct {
    jobject jArray;
    jsize length;
    unsigned char *table;
} LookupArrayInfo;

#define NLUT 8

#ifdef _LITTLE_ENDIAN
#define INDEXES    { 3, 2, 1, 0, 7, 6, 5, 4 }
#else
#define INDEXES    { 0, 1, 2, 3, 4, 5, 6, 7 }
#endif

static int lookupShortData(mlib_image* src, mlib_image* dst,
    LookupArrayInfo* lookup)
{
    int x, y;
    unsigned int mask = NLUT-1;

    unsigned short* srcLine = (unsigned short*)src->data;
    unsigned char* dstLine = (unsigned char*)dst->data;

    static int indexes[NLUT] = INDEXES;

    if (src->width != dst->width || src->height != dst->height) {
        return 0;
    }

    for (y=0; y < src->height; y++) {
        int nloop, nx;
        int npix = src->width;

        unsigned short* srcPixel = srcLine;
        unsigned char* dstPixel = dstLine;

#ifdef SIMPLE_LOOKUP_LOOP
        for (x=0; status && x < width; x++) {
            unsigned short s = *srcPixel++;
            if (s >= lookup->length) {
                /* we can not handle source image using
                * byte lookup table. Fall back to processing
                * images in java
                */
                return 0;
            }
            *dstPixel++ = lookup->table[s];
        }
#else
        /* Get to 32 bit-aligned point */
        while(((uintptr_t)dstPixel & 0x3) != 0 && npix>0) {
            unsigned short s = *srcPixel++;
            if (s >= lookup->length) {
                return 0;
            }
            *dstPixel++ = lookup->table[s];
            npix--;
        }

        /*
         * Do NLUT pixels per loop iteration.
         * Pack into ints and write out 2 at a time.
         */
        nloop = npix/NLUT;
        nx = npix%NLUT;

        for(x=nloop; x!=0; x--) {
            int i = 0;
            int* dstP = (int*)dstPixel;

            for (i = 0; i < NLUT; i++) {
                if (srcPixel[i] >= lookup->length) {
                    return 0;
                }
            }

            dstP[0] = (int)
                ((lookup->table[srcPixel[indexes[0]]] << 24) |
                 (lookup->table[srcPixel[indexes[1]]] << 16) |
                 (lookup->table[srcPixel[indexes[2]]] << 8)  |
                  lookup->table[srcPixel[indexes[3]]]);
            dstP[1] = (int)
                ((lookup->table[srcPixel[indexes[4]]] << 24) |
                 (lookup->table[srcPixel[indexes[5]]] << 16) |
                 (lookup->table[srcPixel[indexes[6]]] << 8)  |
                  lookup->table[srcPixel[indexes[7]]]);


            dstPixel += NLUT;
            srcPixel += NLUT;
        }

        /*
         * Complete any remaining pixels
         */
        for(x=nx; x!=0; x--) {
            unsigned short s = *srcPixel++;
            if (s >= lookup->length) {
                return 0;
            }
            *dstPixel++ = lookup->table[s];
        }
#endif

        dstLine += dst->stride;     // array of bytes, scan stride in bytes
        srcLine += src->stride / 2; // array of shorts, scan stride in bytes
    }
    return 1;
}

JNIEXPORT jint JNICALL
Java_sun_awt_image_ImagingLib_lookupByteBI(JNIEnv *env, jobject thisLib,
                                           jobject jsrc, jobject jdst,
                                           jobjectArray jtableArrays)
{
    mlib_image *src;
    mlib_image *dst;
    void *sdata, *ddata;
    unsigned char **tbl;
    unsigned char lut[256];
    int retStatus = 1;
    int i;
    mlib_status status;
    int lut_nbands;
    LookupArrayInfo *jtable;
    BufImageS_t *srcImageP, *dstImageP;
    int nbands;
    int ncomponents;
    mlibHintS_t hint;

    /* This function requires a lot of local refs ??? Is 64 enough ??? */
    if ((*env)->EnsureLocalCapacity(env, 64) < 0)
        return 0;

    if (s_nomlib) return 0;
    if (s_timeIt) (*start_timer)(3600);

    /* Parse the source image */
    if (awt_parseImage(env, jsrc, &srcImageP, FALSE) <= 0) {
        /* Can't handle any custom images */
        return 0;
    }

    /* Parse the destination image */
    if (awt_parseImage(env, jdst, &dstImageP, FALSE) <= 0) {
        /* Can't handle any custom images */
        awt_freeParsedImage(srcImageP, TRUE);
        return 0;
    }

    nbands = setImageHints(env, srcImageP, dstImageP, FALSE, TRUE,
                        FALSE, &hint);

    if (nbands < 1 || nbands > srcImageP->cmodel.numComponents) {
        /* Can't handle any custom images */
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        return 0;
    }

    ncomponents = srcImageP->cmodel.isDefaultCompatCM
        ? 4
        : srcImageP->cmodel.numComponents;

    /* Make sure that color order can be used for
     * re-ordering of lookup arrays.
     */
    for (i = 0; i < nbands; i++) {
        int idx = srcImageP->hints.colorOrder[i];

        if (idx < 0 || idx >= ncomponents) {
            awt_freeParsedImage(srcImageP, TRUE);
            awt_freeParsedImage(dstImageP, TRUE);
            return 0;
        }
    }

    lut_nbands = (*env)->GetArrayLength(env, jtableArrays);

    if (lut_nbands > ncomponents) {
        lut_nbands = ncomponents;
    }

    tbl = NULL;
    if (SAFE_TO_ALLOC_2(ncomponents, sizeof(unsigned char *))) {
        tbl = (unsigned char **)
            calloc(1, ncomponents * sizeof(unsigned char *));
    }

    jtable = NULL;
    if (SAFE_TO_ALLOC_2(lut_nbands, sizeof(LookupArrayInfo))) {
        jtable = (LookupArrayInfo *)malloc(lut_nbands * sizeof (LookupArrayInfo));
    }

    if (tbl == NULL || jtable == NULL) {
        if (tbl != NULL) free(tbl);
        if (jtable != NULL) free(jtable);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        JNU_ThrowNullPointerException(env, "NULL LUT");
        return 0;
    }
    /* Need to grab these pointers before we lock down arrays */
    for (i=0; i < lut_nbands; i++) {
        jtable[i].jArray = (*env)->GetObjectArrayElement(env, jtableArrays, i);

        if (jtable[i].jArray != NULL) {
            jtable[i].length = (*env)->GetArrayLength(env, jtable[i].jArray);
            jtable[i].table = NULL;

            if (jtable[i].length < 256) {
                /* we may read outside the table during lookup */
                jtable[i].jArray = NULL;
                jtable[i].length = 0;
            }
        }
        if (jtable[i].jArray == NULL) {
            free(tbl);
            free(jtable);
            awt_freeParsedImage(srcImageP, TRUE);
            awt_freeParsedImage(dstImageP, TRUE);
            return 0;
        }
    }

    /* Allocate the arrays */
    if (allocateArray(env, srcImageP, &src, &sdata, TRUE, FALSE, FALSE) < 0) {
        /* Must be some problem */
        free(tbl);
        free(jtable);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        return 0;
    }
    if (allocateArray(env, dstImageP, &dst, &ddata, FALSE, FALSE, FALSE) < 0) {
        /* Must be some problem */
        free(tbl);
        free(jtable);
        freeArray(env, srcImageP, src, sdata, NULL, NULL, NULL);
        awt_freeParsedImage(srcImageP, TRUE);
        awt_freeParsedImage(dstImageP, TRUE);
        return 0;
    }

    /* Set up a straight lut so we don't mess around with alpha */
    /*
     * NB: medialib lookup routine expects lookup array for each
     * component of source image including alpha.
     * If lookup table we got form the java layer does not contain
     * sufficient number of lookup arrays we add references to identity
     * lookup array to make medialib happier.
     */
    if (lut_nbands < ncomponents) {
        int j;
        /* REMIND: This should be the size of the input lut!! */
        for (j=0; j < 256; j++) {
            lut[j] = j;
        }
        for (j=0; j < ncomponents; j++) {
            tbl[j] = lut;
        }
    }

    for (i=0; i < lut_nbands; i++) {
        jtable[i].table = (unsigned char *)
            (*env)->GetPrimitiveArrayCritical(env, jtable[i].jArray, NULL);
        if (jtable[i].table == NULL) {
            /* Free what we've got so far. */
            int j;
            for (j = 0; j < i; j++) {
                (*env)->ReleasePrimitiveArrayCritical(env,
                                                      jtable[j].jArray,
                                                      (jbyte *) jtable[j].table,
                                                      JNI_ABORT);
            }
            free(tbl);
            free(jtable);
            freeArray(env, srcImageP, src, sdata, NULL, NULL, NULL);
            awt_freeParsedImage(srcImageP, TRUE);
            awt_freeParsedImage(dstImageP, TRUE);
            return 0;
        }
        tbl[srcImageP->hints.colorOrder[i]] = jtable[i].table;
    }

    if (lut_nbands == 1) {
        for (i=1; i < nbands -
                 srcImageP->cmodel.supportsAlpha; i++) {
                     tbl[srcImageP->hints.colorOrder[i]] = jtable[0].table;
        }
    }

    /* Mlib needs 16bit lookuptable and must be signed! */
    if (src->type == MLIB_SHORT) {
        if (dst->type == MLIB_BYTE) {
            if (nbands > 1) {
                retStatus = 0;
            }
            else {
                retStatus = lookupShortData(src, dst, &jtable[0]);
            }
        }
        /* How about ddata == null? */
    }
    else if ((status = (*sMlibFns[MLIB_LOOKUP].fptr)(dst, src,
                                      (void **)tbl) != MLIB_SUCCESS)) {
        printMedialibError(status);
        retStatus = 0;
    }

   /* Release the LUT */
    for (i=0; i < lut_nbands; i++) {
        (*env)->ReleasePrimitiveArrayCritical(env, jtable[i].jArray,
            (jbyte *) jtable[i].table, JNI_ABORT);
    }
    free ((void *) jtable);
    free ((void *) tbl);

    /*
     * Means that we couldn't write directly into
     * the destination buffer
     */
    if (ddata == NULL) {

        /* Need to store it back into the array */
        if (storeImageArray(env, srcImageP, dstImageP, dst) < 0) {
            /* Error */
            retStatus = 0;
        }
    }


    /* Release the pinned memory */
    freeArray(env, srcImageP, src, sdata, dstImageP, dst, ddata);

    awt_freeParsedImage(srcImageP, TRUE);
    awt_freeParsedImage(dstImageP, TRUE);

    if (s_timeIt) (*stop_timer)(3600, 1);

    return retStatus;
}

JNIEXPORT jint JNICALL
Java_sun_awt_image_ImagingLib_lookupByteRaster(JNIEnv *env,
                                               jobject this,
                                               jobject jsrc,
                                               jobject jdst,
                                               jobjectArray jtableArrays)
{
    RasterS_t*     srcRasterP;
    RasterS_t*     dstRasterP;
    mlib_image*    src;
    mlib_image*    dst;
    void*          sdata;
    void*          ddata;
    LookupArrayInfo jtable[4];
    unsigned char* mlib_lookupTable[4];
    int            i;
    int            retStatus = 1;
    mlib_status    status;
    int            jlen;
    int            lut_nbands;
    int            src_nbands;
    int            dst_nbands;
    unsigned char  ilut[256];

    /* This function requires a lot of local refs ??? Is 64 enough ??? */
    if ((*env)->EnsureLocalCapacity(env, 64) < 0)
        return 0;

    if (s_nomlib) return 0;
    if (s_timeIt) (*start_timer)(3600);

    if ((srcRasterP = (RasterS_t*) calloc(1, sizeof(RasterS_t))) == NULL) {
        JNU_ThrowOutOfMemoryError(env, "Out of memory");
        return -1;
    }

    if ((dstRasterP = (RasterS_t *) calloc(1, sizeof(RasterS_t))) == NULL) {
        JNU_ThrowOutOfMemoryError(env, "Out of memory");
        free(srcRasterP);
        return -1;
    }

    /* Parse the source raster - reject custom images */
    if (awt_parseRaster(env, jsrc, srcRasterP) <= 0) {
        free(srcRasterP);
        free(dstRasterP);
        return 0;
    }

    /* Parse the destination image - reject custom images */
    if (awt_parseRaster(env, jdst, dstRasterP) <= 0) {
        awt_freeParsedRaster(srcRasterP, TRUE);
        free(dstRasterP);
        return 0;
    }

    jlen = (*env)->GetArrayLength(env, jtableArrays);

    lut_nbands = jlen;
    src_nbands = srcRasterP->numBands;
    dst_nbands = dstRasterP->numBands;

    /* adjust number of lookup bands */
    if (lut_nbands > src_nbands) {
        lut_nbands = src_nbands;
    }

    /* MediaLib can't do more than 4 bands */
    if (src_nbands <= 0 || src_nbands > 4 ||
        dst_nbands <= 0 || dst_nbands > 4 ||
        lut_nbands <= 0 || lut_nbands > 4 ||
        src_nbands != dst_nbands ||
        ((lut_nbands != 1) && (lut_nbands != src_nbands)))
    {
        // we should free parsed rasters here
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        return 0;
    }

    /* Allocate the raster arrays */
    if (allocateRasterArray(env, srcRasterP, &src, &sdata, TRUE) < 0) {
        /* Must be some problem */
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        return 0;
    }
    if (allocateRasterArray(env, dstRasterP, &dst, &ddata, FALSE) < 0) {
        /* Must be some problem */
        freeDataArray(env, srcRasterP->jdata, src, sdata, NULL, NULL, NULL);
        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        return 0;
    }

    /*
     * Well, until now we have analyzed number of bands in
     * src and dst rasters.
     * However, it is not enough because medialib lookup routine uses
     * number of channels of medialib image. Note that in certain
     * case number of channels may differs form the number of bands.
     * Good example is raster that is used in TYPE_INT_RGB buffered
     * image: it has 3 bands, but their medialib representation has
     * 4 channels.
     *
     * In order to avoid the lookup routine failure, we need:
     *
     * 1. verify that src and dst have same number of channels.
     * 2. provide lookup array for every channel. If we have "extra"
     *    channel (like the raster described above) then we need to
     *    provide identical lookup array.
     */
    if (src->channels != dst->channels) {
        freeDataArray(env, srcRasterP->jdata, src, sdata,
                      dstRasterP->jdata, dst, ddata);

        awt_freeParsedRaster(srcRasterP, TRUE);
        awt_freeParsedRaster(dstRasterP, TRUE);
        return 0;
    }

    if (src_nbands < src->channels) {
        for (i = 0; i < 256; i++) {
            ilut[i] = i;
        }
    }


    /* Get references to the lookup table arrays */
    /* Need to grab these pointers before we lock down arrays */
    for (i=0; i < lut_nbands; i++) {
        jtable[i].jArray = (*env)->GetObjectArrayElement(env, jtableArrays, i);
        jtable[i].table = NULL;
        if (jtable[i].jArray != NULL) {
            jtable[i].length = (*env)->GetArrayLength(env, jtable[i].jArray);
            if (jtable[i].length < 256) {
                 /* we may read outside the table during lookup */
                jtable[i].jArray = NULL;
            }
        }

        if (jtable[i].jArray == NULL)
        {
            freeDataArray(env, srcRasterP->jdata, src, sdata,
                          dstRasterP->jdata, dst, ddata);

            awt_freeParsedRaster(srcRasterP, TRUE);
            awt_freeParsedRaster(dstRasterP, TRUE);
            return 0;
        }
    }

    for (i=0; i < lut_nbands; i++) {
        jtable[i].table = (unsigned char *)
            (*env)->GetPrimitiveArrayCritical(env, jtable[i].jArray, NULL);
        if (jtable[i].table == NULL) {
            /* Free what we've got so far. */
            int j;
            for (j = 0; j < i; j++) {
                (*env)->ReleasePrimitiveArrayCritical(env,
                                                      jtable[j].jArray,
                                                      (jbyte *) jtable[j].table,
                                                      JNI_ABORT);
            }
            freeDataArray(env, srcRasterP->jdata, src, sdata,
                          dstRasterP->jdata, dst, ddata);
            awt_freeParsedRaster(srcRasterP, TRUE);
            awt_freeParsedRaster(dstRasterP, TRUE);
            return 0;
        }
        mlib_lookupTable[i] = jtable[i].table;
    }

    /*
     * Medialib routine expects lookup array for each band of raster.
     * Setup the  rest of lookup arrays if supplied lookup table
     * contains single lookup array.
     */
    for (i = lut_nbands; i < src_nbands; i++) {
        mlib_lookupTable[i] = jtable[0].table;
    }

    /*
     * Setup lookup array for "extra" channels
     */
    for ( ; i < src->channels; i++) {
        mlib_lookupTable[i] = ilut;
    }

    /* Mlib needs 16bit lookuptable and must be signed! */
    if (src->type == MLIB_SHORT) {
        if (dst->type == MLIB_BYTE) {
            if (lut_nbands > 1) {
                retStatus = 0;
            } else {
                retStatus = lookupShortData(src, dst, &jtable[0]);
            }
        }
        /* How about ddata == null? */
    } else if ((status = (*sMlibFns[MLIB_LOOKUP].fptr)(dst, src,
                                      (void **)mlib_lookupTable) != MLIB_SUCCESS)) {
        printMedialibError(status);
        retStatus = 0;
    }

    /* Release the LUT */
    for (i=0; i < lut_nbands; i++) {
        (*env)->ReleasePrimitiveArrayCritical(env, jtable[i].jArray,
                                              (jbyte *) jtable[i].table, JNI_ABORT);
    }

    /*
     * Means that we couldn't write directly into
     * the destination buffer
     */
    if (ddata == NULL) {
        if (storeRasterArray(env, srcRasterP, dstRasterP, dst) < 0) {
            retStatus = setPixelsFormMlibImage(env, dstRasterP, dst);
        }
    }

    /* Release the pinned memory */
    freeDataArray(env, srcRasterP->jdata, src, sdata,
                  dstRasterP->jdata, dst, ddata);

    awt_freeParsedRaster(srcRasterP, TRUE);
    awt_freeParsedRaster(dstRasterP, TRUE);

    if (s_timeIt) (*stop_timer)(3600, 1);

    return retStatus;
}


JNIEXPORT jboolean JNICALL
Java_sun_awt_image_ImagingLib_init(JNIEnv *env, jclass thisClass) {
    char *start;
    if (getenv("IMLIB_DEBUG")) {
        start_timer = awt_setMlibStartTimer();
        stop_timer = awt_setMlibStopTimer();
        if (start_timer && stop_timer) {
            s_timeIt = 1;
        }
    }

    if (getenv("IMLIB_PRINT")) {
        s_printIt = 1;
    }
    if ((start = getenv("IMLIB_START")) != NULL) {
        sscanf(start, "%d", &s_startOff);
    }

    if (getenv ("IMLIB_NOMLIB")) {
        s_nomlib = 1;
        return JNI_FALSE;
    }

    /* This function is platform-dependent and is in awt_mlib.c */
    if (awt_getImagingLib(env, (mlibFnS_t *)&sMlibFns, &sMlibSysFns) !=
        MLIB_SUCCESS)
    {
        s_nomlib = 1;
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/* REMIND: How to specify border? */
static void extendEdge(JNIEnv *env, BufImageS_t *imageP,
                       int *widthP, int *heightP) {
    RasterS_t *rasterP = &imageP->raster;
    int width;
    int height;
    /* Useful for convolution? */

    jobject jbaseraster = (*env)->GetObjectField(env, rasterP->jraster,
                                                 g_RasterBaseRasterID);
    width = rasterP->width;
    height = rasterP->height;
#ifdef WORKING
    if (! JNU_IsNull(env, jbaseraster) &&
        !(*env)->IsSameObject(env, rasterP->jraster, jbaseraster)) {
        int xOff;
        int yOff;
        int baseWidth;
        int baseHeight;
        int baseXoff;
        int baseYoff;
        /* Not the same object so get the width and height */
        xOff = (*env)->GetIntField(env, rasterP->jraster, g_RasterXOffsetID);
        yOff = (*env)->GetIntField(env, rasterP->jraster, g_RasterYOffsetID);
        baseWidth  = (*env)->GetIntField(env, jbaseraster, g_RasterWidthID);
        baseHeight = (*env)->GetIntField(env, jbaseraster, g_RasterHeightID);
        baseXoff   = (*env)->GetIntField(env, jbaseraster, g_RasterXOffsetID);
        baseYoff   = (*env)->GetIntField(env, jbaseraster, g_RasterYOffsetID);

        if (xOff + rasterP->width < baseXoff + baseWidth) {
            /* Can use edge */
            width++;
        }
        if (yOff + rasterP->height < baseYoff + baseHeight) {
            /* Can use edge */
            height++;
        }

    }
#endif

}

static int
setImageHints(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP,
              int expandICM, int useAlpha,
              int premultiply, mlibHintS_t *hintP)
{
    ColorModelS_t *srcCMP = &srcP->cmodel;
    ColorModelS_t *dstCMP = &dstP->cmodel;
    int nbands = 0;
    int ncomponents;

    hintP->dataType = srcP->raster.dataType;
    hintP->addAlpha = FALSE;

    /* Are the color spaces the same? */
    if (srcCMP->csType != dstCMP->csType) {
        /* If the src is GRAY and dst RGB, we can handle it */
        if (!(srcCMP->csType == java_awt_color_ColorSpace_TYPE_GRAY &&
              dstCMP->csType == java_awt_color_ColorSpace_TYPE_RGB)) {
            /* Nope, need to handle that in java for now */
            return -1;
        }
        else {
            hintP->cvtSrcToDefault = TRUE;
        }
    }
    else {
        if (srcP->hints.needToExpand) {
            hintP->cvtSrcToDefault = TRUE;
        }
        else {
            /* Need to initialize this */
            hintP->cvtSrcToDefault = FALSE;
        }
    }

    ncomponents = srcCMP->numComponents;
    if ((useAlpha == 0) && srcCMP->supportsAlpha) {
        ncomponents--;  /* ?? */
        /* Not really, more like shrink src to get rid of alpha */
        hintP->cvtSrcToDefault = TRUE;
    }

    hintP->dataType = srcP->raster.dataType;
    if (hintP->cvtSrcToDefault == FALSE) {
        if (srcCMP->cmType == INDEX_CM_TYPE) {
            if (expandICM) {
                nbands = srcCMP->numComponents;
                hintP->cvtSrcToDefault = TRUE;

                if (dstCMP->isDefaultCompatCM) {
                    hintP->allocDefaultDst = FALSE;
                    hintP->cvtToDst = FALSE;
                }
                else if (dstCMP->isDefaultCompatCM) {
                    hintP->allocDefaultDst = FALSE;
                    hintP->cvtToDst = FALSE;
                }
            }
            else {
                nbands = 1;
                hintP->cvtSrcToDefault = FALSE;
            }

        }
        else {
            if (srcP->hints.packing & INTERLEAVED) {
                nbands = srcCMP->numComponents;
            }
            else {
                nbands = 1;
            }

            /* Look at the packing */
            if ((srcP->hints.packing&BYTE_INTERLEAVED)==BYTE_INTERLEAVED ||
                (srcP->hints.packing&SHORT_INTERLEAVED)==SHORT_INTERLEAVED||
                (srcP->hints.packing&BYTE_SINGLE_BAND) == BYTE_SINGLE_BAND||
                (srcP->hints.packing&SHORT_SINGLE_BAND)==SHORT_SINGLE_BAND||
                (srcP->hints.packing&BYTE_BANDED)  == BYTE_BANDED       ||
                (srcP->hints.packing&SHORT_BANDED) == SHORT_BANDED) {
                /* Can use src directly */
                hintP->cvtSrcToDefault = FALSE;
            }
            else {
                /* Must be packed or custom */
                hintP->cvtSrcToDefault = TRUE;
            }
        }
    }
    if (hintP->cvtSrcToDefault) {
        /* By definition */
        nbands = 4;  /* What about alpha? */
        hintP->dataType = BYTE_DATA_TYPE;
        hintP->needToCopy = TRUE;

        if (srcP->imageType == dstP->imageType) {
            hintP->cvtToDst = TRUE;
        }
        else if (dstP->cmodel.isDefaultCM) {
            /* Not necessarily */
            hintP->cvtToDst = FALSE;
        }
        else {
            hintP->cvtToDst = TRUE;
        }
    }
    else {
        int srcImageType = srcP->imageType;
        int dstImageType = dstP->imageType;
        /* Special case where we need to fill in alpha values */
        if (srcCMP->isDefaultCompatCM && dstCMP->isDefaultCompatCM) {
            int i;
            if (!srcCMP->supportsAlpha &&dstCMP->supportsAlpha) {
                hintP->addAlpha = TRUE;
            }
            for (i=0; i < srcCMP->numComponents; i++) {
                if (srcP->hints.colorOrder[i] != dstP->hints.colorOrder[i]){
                    if (!srcCMP->isDefaultCM) {
                        hintP->cvtSrcToDefault = TRUE;
                        srcImageType = java_awt_image_BufferedImage_TYPE_INT_ARGB;
                    }
                    if (!dstCMP->isDefaultCM) {
                        hintP->cvtToDst = TRUE;
                        dstImageType = java_awt_image_BufferedImage_TYPE_INT_ARGB;
                    }

                    break;
                }
            }
        }
        else if (srcCMP->cmType != INDEX_CM_TYPE &&
                 !srcCMP->supportsAlpha && dstCMP->supportsAlpha)
        {
            /* We've already handled the index case.  This is for the rest of the cases */
            srcImageType = java_awt_image_BufferedImage_TYPE_INT_ARGB;
            hintP->cvtSrcToDefault = TRUE;
        }

        hintP->allocDefaultDst = FALSE;
        if (srcImageType == dstImageType) {
            /* Same image type so use it */
            hintP->cvtToDst = FALSE;
        }
        else if (srcImageType == TYPE_INT_RGB &&
                 (dstImageType == TYPE_INT_ARGB ||
                  dstImageType == TYPE_INT_ARGB_PRE)) {
            hintP->cvtToDst = FALSE;
        }
        else if (srcImageType == TYPE_INT_BGR &&
                 (dstImageType == TYPE_4BYTE_ABGR ||
                  dstImageType == TYPE_4BYTE_ABGR_PRE)) {
            hintP->cvtToDst = FALSE;
        }
        else if (srcP->hints.packing == dstP->hints.packing) {
            /* Now what? */

            /* Check color order */

            /* Check if just need to scale the data */

            hintP->cvtToDst = TRUE;
        }
        else {
            /* Don't know what it is so convert it */
            hintP->allocDefaultDst = TRUE;
            hintP->cvtToDst = TRUE;
        }
        hintP->needToCopy = (ncomponents > nbands);
    }

    return nbands;
}


static int
expandPacked(JNIEnv *env, BufImageS_t *img, ColorModelS_t *cmP,
             RasterS_t *rasterP, int component, unsigned char *bdataP) {

    if (rasterP->rasterType == COMPONENT_RASTER_TYPE) {
        switch (rasterP->dataType) {
        case BYTE_DATA_TYPE:
            if (expandPackedBCR(env, rasterP, component, bdataP) < 0) {
                /* Must have been an error */
                return -1;
            }
            break;

        case SHORT_DATA_TYPE:
            if (expandPackedICR(env, rasterP, component, bdataP) < 0) {
                /* Must have been an error */
                return -1;
            }
            break;

        case INT_DATA_TYPE:
            if (expandPackedICR(env, rasterP, component, bdataP) < 0) {
                /* Must have been an error */
                return -1;
            }
            break;

        default:
            /* REMIND: Return some sort of error */
            return -1;
        }
    }
    else {
        /* REMIND: Return some sort of error */
        return -1;
    }

    return 0;
}

#define NUM_LINES    10

static int
cvtCustomToDefault(JNIEnv *env, BufImageS_t *imageP, int component,
                   unsigned char *dataP) {
    const RasterS_t *rasterP = &imageP->raster;
    const int w = rasterP->width;
    const int h = rasterP->height;

    int y;
    jintArray jpixels = NULL;
    jint *pixels;
    unsigned char *dP = dataP;
    int numLines = h > NUM_LINES ? NUM_LINES : h;

    /* it is safe to calculate the scan length, because width has been verified
     * on creation of the mlib image
     */
    const int scanLength = w * 4;

    int nbytes = 0;
    if (!SAFE_TO_MULT(numLines, scanLength)) {
        return -1;
    }

    nbytes = numLines * scanLength;

    jpixels = (*env)->NewIntArray(env, nbytes);
    if (JNU_IsNull(env, jpixels)) {
        (*env)->ExceptionClear(env);
        JNU_ThrowOutOfMemoryError(env, "Out of Memory");
        return -1;
    }

    for (y = 0; y < h; y += numLines) {
        if (y + numLines > h) {
            numLines = h - y;
            nbytes = numLines * scanLength;
        }

        (*env)->CallObjectMethod(env, imageP->jimage,
                                 g_BImgGetRGBMID, 0, y,
                                 w, numLines,
                                 jpixels, 0, w);
        if ((*env)->ExceptionOccurred(env)) {
            (*env)->DeleteLocalRef(env, jpixels);
            return -1;
        }

        pixels = (*env)->GetPrimitiveArrayCritical(env, jpixels, NULL);
        if (pixels == NULL) {
            (*env)->DeleteLocalRef(env, jpixels);
            return -1;
        }

        memcpy(dP, pixels, nbytes);
        dP += nbytes;

        (*env)->ReleasePrimitiveArrayCritical(env, jpixels, pixels,
                                              JNI_ABORT);
    }

    /* Need to release the array */
    (*env)->DeleteLocalRef(env, jpixels);

    return 0;
}

static int
cvtDefaultToCustom(JNIEnv *env, BufImageS_t *imageP, int component,
                   unsigned char *dataP) {
    const RasterS_t *rasterP = &imageP->raster;
    const int w = rasterP->width;
    const int h = rasterP->height;

    int y;
    jintArray jpixels = NULL;
    jint *pixels;
    unsigned char *dP = dataP;
    int numLines = h > NUM_LINES ? NUM_LINES : h;

    /* it is safe to calculate the scan length, because width has been verified
     * on creation of the mlib image
     */
    const int scanLength = w * 4;

    int nbytes = 0;
    if (!SAFE_TO_MULT(numLines, scanLength)) {
        return -1;
    }

    nbytes = numLines * scanLength;

    jpixels = (*env)->NewIntArray(env, nbytes);
    if (JNU_IsNull(env, jpixels)) {
        (*env)->ExceptionClear(env);
        JNU_ThrowOutOfMemoryError(env, "Out of Memory");
        return -1;
    }

    for (y = 0; y < h; y += numLines) {
        if (y + numLines > h) {
            numLines = h - y;
            nbytes = numLines * scanLength;
        }

        pixels = (*env)->GetPrimitiveArrayCritical(env, jpixels, NULL);
        if (pixels == NULL) {
            (*env)->DeleteLocalRef(env, jpixels);
            return -1;
        }

        memcpy(pixels, dP, nbytes);
        dP += nbytes;

       (*env)->ReleasePrimitiveArrayCritical(env, jpixels, pixels, 0);

       (*env)->CallVoidMethod(env, imageP->jimage, g_BImgSetRGBMID, 0, y,
                                w, numLines, jpixels,
                                0, w);
       if ((*env)->ExceptionOccurred(env)) {
           (*env)->DeleteLocalRef(env, jpixels);
           return -1;
       }
    }

    /* Need to release the array */
    (*env)->DeleteLocalRef(env, jpixels);

    return 0;
}

static int
allocateArray(JNIEnv *env, BufImageS_t *imageP,
              mlib_image **mlibImagePP, void **dataPP, int isSrc,
              int cvtToDefault, int addAlpha) {
    void *dataP;
    unsigned char *cDataP;
    RasterS_t *rasterP = &imageP->raster;
    ColorModelS_t *cmP = &imageP->cmodel;
    int dataType = BYTE_DATA_TYPE;
    int width;
    int height;
    HintS_t *hintP = &imageP->hints;
    *dataPP = NULL;

    width = rasterP->width;
    height = rasterP->height;

    /* Useful for convolution? */
    /* This code is zero'ed out so that it cannot be called */

    /* To do this correctly, we need to expand src and dst in the     */
    /* same direction up/down/left/right only if both can be expanded */
    /* in that direction.  Expanding right and down is easy -         */
    /* increment width.  Expanding top and left requires bumping      */
    /* around pointers and incrementing the width/height              */

#if 0
    if (0 && useEdges) {
        baseWidth  = rasterP->baseRasterWidth;
        baseHeight = rasterP->baseRasterHeight;
        baseXoff = rasterP->baseOriginX;
        baseYoff = rasterP->baseOriginY;

        if (rasterP->minX + rasterP->width < baseXoff + baseWidth) {
            /* Can use edge */
            width++;
        }
        if (rasterP->minY + rasterP->height < baseYoff + baseHeight) {
            /* Can use edge */
            height++;
        }

        if (rasterP->minX > baseXoff ) {
            /* Can use edge */
            width++;
            /* NEED TO BUMP POINTER BACK A PIXELSTRIDE */
        }
        if (rasterP->minY  > baseYoff) {
            /* Can use edge */
            height++;
            /* NEED TO BUMP POINTER BACK A SCANLINE */
        }


    }
#endif
    if (cvtToDefault) {
        int status = 0;
        *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, 4, width, height);
        if (*mlibImagePP == NULL) {
            return -1;
        }
        cDataP  = (unsigned char *) mlib_ImageGetData(*mlibImagePP);
        /* Make sure the image is cleared.
         * NB: the image dimension is already verified, so we can
         * safely calculate the length of the buffer.
         */
        memset(cDataP, 0, width*height*4);

        if (!isSrc) {
            return 0;
        }

        switch(imageP->cmodel.cmType) {
        case INDEX_CM_TYPE:
            /* REMIND: Need to rearrange according to dst cm */
            /* Fix 4213160, 4184283 */
            if (rasterP->rasterType == COMPONENT_RASTER_TYPE) {
                return expandICM(env, imageP, (unsigned int *)cDataP);
            }
            else {
                return cvtCustomToDefault(env, imageP, -1, cDataP);
            }

        case DIRECT_CM_TYPE:
            switch(imageP->raster.dataType) {
            case BYTE_DATA_TYPE:
                return expandPackedBCRdefault(env, rasterP, -1, cDataP,
                                              !imageP->cmodel.supportsAlpha);
            case SHORT_DATA_TYPE:
                return expandPackedSCRdefault(env, rasterP, -1, cDataP,
                                              !imageP->cmodel.supportsAlpha);
            case INT_DATA_TYPE:
                return expandPackedICRdefault(env, rasterP, -1, cDataP,
                                              !imageP->cmodel.supportsAlpha);
            }
        } /* switch(imageP->cmodel.cmType) */

        return cvtCustomToDefault(env, imageP, -1, cDataP);
    }

    /* Interleaved with shared data */
    dataP = (void *) (*env)->GetPrimitiveArrayCritical(env, rasterP->jdata,
                                                       NULL);
    if (dataP == NULL) {
        return -1;
    }

    /* Means we need to fill in alpha */
    if (!cvtToDefault && addAlpha) {
        *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, 4, width, height);
        if (*mlibImagePP != NULL) {
            unsigned int *dstP  = (unsigned int *)
                mlib_ImageGetData(*mlibImagePP);
            int dstride = (*mlibImagePP)->stride>>2;
            int sstride = hintP->sStride>>2;
            unsigned int *srcP = (unsigned int *)
                ((unsigned char *)dataP + hintP->dataOffset);
            unsigned int *dP, *sP;
            int x, y;
            for (y=0; y < height; y++, srcP += sstride, dstP += dstride){
                sP = srcP;
                dP = dstP;
                for (x=0; x < width; x++) {
                    dP[x] = sP[x] | 0xff000000;
                }
            }
        }
        (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP,
                                              JNI_ABORT);
        return 0;
    }
    else if ((hintP->packing & BYTE_INTERLEAVED) == BYTE_INTERLEAVED) {
        int nChans = (cmP->isDefaultCompatCM ? 4 : hintP->numChans);
        /* Easy case.  It is or is similar to the default CM so use
     * the array.  Must be byte data.
         */
            /* Create the medialib image */
        *mlibImagePP = (*sMlibSysFns.createStructFP)(MLIB_BYTE,
                                              nChans,
                                              width,
                                              height,
                                              hintP->sStride,
                                              (unsigned char *)dataP
                                              + hintP->dataOffset);
    }
    else if ((hintP->packing & SHORT_INTERLEAVED) == SHORT_INTERLEAVED) {
        *mlibImagePP = (*sMlibSysFns.createStructFP)(MLIB_SHORT,
                                              hintP->numChans,
                                              width,
                                              height,
                                              imageP->raster.scanlineStride*2,
                                              (unsigned short *)dataP
                                              + hintP->channelOffset);
    }
    else {
        /* Release the data array */
        (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP,
                                              JNI_ABORT);
        return -1;
    }

    *dataPP = dataP;
    return 0;
}

static int
allocateRasterArray(JNIEnv *env, RasterS_t *rasterP,
                    mlib_image **mlibImagePP, void **dataPP, int isSrc) {
    void *dataP;
    unsigned char *cDataP;
    int dataType = BYTE_DATA_TYPE;
    int width;
    int height;
    int dataSize;
    int offset;

    *dataPP = NULL;

    width = rasterP->width;
    height = rasterP->height;

    if (rasterP->numBands <= 0 || rasterP->numBands > 4) {
        /* REMIND: Fix this */
        return -1;
    }

    /* Useful for convolution? */
    /* This code is zero'ed out so that it cannot be called */

    /* To do this correctly, we need to expand src and dst in the     */
    /* same direction up/down/left/right only if both can be expanded */
    /* in that direction.  Expanding right and down is easy -         */
    /* increment width.  Expanding top and left requires bumping      */
    /* around pointers and incrementing the width/height              */

#if 0
    if (0 && useEdges) {
        baseWidth  = rasterP->baseRasterWidth;
        baseHeight = rasterP->baseRasterHeight;
        baseXoff = rasterP->baseOriginX;
        baseYoff = rasterP->baseOriginY;

        if (rasterP->minX + rasterP->width < baseXoff + baseWidth) {
            /* Can use edge */
            width++;
        }
        if (rasterP->minY + rasterP->height < baseYoff + baseHeight) {
            /* Can use edge */
            height++;
        }

        if (rasterP->minX > baseXoff ) {
            /* Can use edge */
            width++;
            /* NEED TO BUMP POINTER BACK A PIXELSTRIDE */
        }
        if (rasterP->minY  > baseYoff) {
            /* Can use edge */
            height++;
            /* NEED TO BUMP POINTER BACK A SCANLINE */
        }


    }
#endif
    switch (rasterP->type) {
    case sun_awt_image_IntegerComponentRaster_TYPE_INT_8BIT_SAMPLES:
        if (!((rasterP->chanOffsets[0] == 0 || SAFE_TO_ALLOC_2(rasterP->chanOffsets[0], 4)) &&
              SAFE_TO_ALLOC_2(width, 4) &&
              SAFE_TO_ALLOC_3(height, rasterP->scanlineStride, 4)))
        {
            return -1;
        }
        offset = 4 * rasterP->chanOffsets[0];
        dataSize = 4 * (*env)->GetArrayLength(env, rasterP->jdata);

        if (offset < 0 || offset >= dataSize ||
            width > rasterP->scanlineStride ||
            height * rasterP->scanlineStride * 4 > dataSize - offset)
        {
            // raster data buffer is too short
            return -1;
        }
        dataP = (void *) (*env)->GetPrimitiveArrayCritical(env, rasterP->jdata,
                                                           NULL);
        if (dataP == NULL) {
            return -1;
        }
        *mlibImagePP = (*sMlibSysFns.createStructFP)(MLIB_BYTE, 4,
                                              width, height,
                                              rasterP->scanlineStride*4,
                                              (unsigned char *)dataP + offset);
        *dataPP = dataP;
        return 0;
    case sun_awt_image_IntegerComponentRaster_TYPE_BYTE_SAMPLES:
        if (!(SAFE_TO_ALLOC_2(width, rasterP->numBands) &&
              SAFE_TO_ALLOC_2(height, rasterP->scanlineStride)))
        {
            return -1;
        }
        offset = rasterP->chanOffsets[0];
        dataSize = (*env)->GetArrayLength(env, rasterP->jdata);

        if (offset < 0 || offset >= dataSize ||
            width * rasterP->numBands > rasterP->scanlineStride ||
            height * rasterP->scanlineStride > dataSize - offset)
        {
            // raster data buffer is too short
            return -1;
        }
        dataP = (void *) (*env)->GetPrimitiveArrayCritical(env, rasterP->jdata,
                                                           NULL);
        if (dataP == NULL) {
            return -1;
        }
        *mlibImagePP = (*sMlibSysFns.createStructFP)(MLIB_BYTE, rasterP->numBands,
                                              width, height,
                                              rasterP->scanlineStride,
                                              (unsigned char *)dataP + offset);
        *dataPP = dataP;
        return 0;
    case sun_awt_image_IntegerComponentRaster_TYPE_USHORT_SAMPLES:
        if (!((rasterP->chanOffsets[0] == 0 || SAFE_TO_ALLOC_2(rasterP->chanOffsets[0], 2)) &&
              SAFE_TO_ALLOC_3(width, rasterP->numBands, 2) &&
              SAFE_TO_ALLOC_3(height, rasterP->scanlineStride, 2)))
        {
              return -1;
        }
        offset = rasterP->chanOffsets[0] * 2;
        dataSize = 2 * (*env)->GetArrayLength(env, rasterP->jdata);

        if (offset < 0 || offset >= dataSize ||
            width * rasterP->numBands > rasterP->scanlineStride ||
            height * rasterP->scanlineStride * 2 > dataSize - offset)
        {
            // raster data buffer is too short
             return -1;
        }
        dataP = (void *) (*env)->GetPrimitiveArrayCritical(env, rasterP->jdata,
                                                           NULL);
        if (dataP == NULL) {
            return -1;
        }
        *mlibImagePP = (*sMlibSysFns.createStructFP)(MLIB_SHORT,
                                                     rasterP->numBands,
                                                     width, height,
                                                     rasterP->scanlineStride*2,
                                                     (unsigned char *)dataP + offset);
        *dataPP = dataP;
        return 0;

    case sun_awt_image_IntegerComponentRaster_TYPE_BYTE_PACKED_SAMPLES:
        *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, rasterP->numBands,
                                        width, height);
        if (*mlibImagePP == NULL) {
            return -1;
        }
        if (!isSrc) return 0;
        cDataP  = (unsigned char *) mlib_ImageGetData(*mlibImagePP);
        return expandPackedBCR(env, rasterP, -1, cDataP);

    case sun_awt_image_IntegerComponentRaster_TYPE_USHORT_PACKED_SAMPLES:
        if (rasterP->sppsm.maxBitSize <= 8) {
            *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, rasterP->numBands,
                                            width, height);
            if (*mlibImagePP == NULL) {
                return -1;
            }
            if (!isSrc) return 0;
            cDataP  = (unsigned char *) mlib_ImageGetData(*mlibImagePP);
            return expandPackedSCR(env, rasterP, -1, cDataP);
        }
        break;
    case sun_awt_image_IntegerComponentRaster_TYPE_INT_PACKED_SAMPLES:
        if (rasterP->sppsm.maxBitSize <= 8) {
            *mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, rasterP->numBands,
                                            width, height);
            if (*mlibImagePP == NULL) {
                return -1;
            }
            if (!isSrc) return 0;
            cDataP  = (unsigned char *) mlib_ImageGetData(*mlibImagePP);
            return expandPackedICR(env, rasterP, -1, cDataP);
        }
        break;
    }

    /* Just expand it right now */
    switch (rasterP->dataType) {
    case BYTE_DATA_TYPE:
        if ((*mlibImagePP = (*sMlibSysFns.createFP)(MLIB_BYTE, rasterP->numBands,
                                             width, height)) == NULL) {
            return -1;
        }
        if (isSrc) {
            if (awt_getPixels(env, rasterP, mlib_ImageGetData(*mlibImagePP)) < 0) {
                (*sMlibSysFns.deleteImageFP)(*mlibImagePP);
                return -1;
            }
        }
        break;

    case SHORT_DATA_TYPE:
        if ((*mlibImagePP = (*sMlibSysFns.createFP)(MLIB_SHORT,
                                                    rasterP->numBands,
                                                    width, height)) == NULL) {
            return -1;
        }
        if (isSrc) {
            if (awt_getPixels(env, rasterP, mlib_ImageGetData(*mlibImagePP)) < 0) {
                (*sMlibSysFns.deleteImageFP)(*mlibImagePP);
                return -1;
            }
        }
        break;

    default:
        return -1;
    }
    return 0;
}

static void
freeArray(JNIEnv *env, BufImageS_t *srcimageP, mlib_image *srcmlibImP,
          void *srcdataP, BufImageS_t *dstimageP, mlib_image *dstmlibImP,
          void *dstdataP) {
    jobject srcJdata = (srcimageP != NULL ? srcimageP->raster.jdata : NULL);
    jobject dstJdata = (dstimageP != NULL ? dstimageP->raster.jdata : NULL);
    freeDataArray(env, srcJdata, srcmlibImP, srcdataP,
                  dstJdata, dstmlibImP, dstdataP);
}
static void
freeDataArray(JNIEnv *env, jobject srcJdata, mlib_image *srcmlibImP,
          void *srcdataP, jobject dstJdata, mlib_image *dstmlibImP,
          void *dstdataP)
{
    /* Free the medialib image */
    if (srcmlibImP) {
        (*sMlibSysFns.deleteImageFP)(srcmlibImP);
    }

    /* Release the array */
    if (srcdataP) {
        (*env)->ReleasePrimitiveArrayCritical(env, srcJdata,
                                              srcdataP, JNI_ABORT);
    }

    /* Free the medialib image */
    if (dstmlibImP) {
        (*sMlibSysFns.deleteImageFP)(dstmlibImP);
    }

    /* Release the array */
    if (dstdataP) {
        (*env)->ReleasePrimitiveArrayCritical(env, dstJdata,
                                              dstdataP, 0);
    }
}

#define ERR_BAD_IMAGE_LAYOUT (-2)

#define CHECK_DST_ARRAY(start_offset, elements_per_scan, elements_per_pixel) \
    do {                                                                     \
        int offset = (start_offset);                                         \
        int lastScanOffset;                                                  \
                                                                             \
        if (!SAFE_TO_MULT((elements_per_scan),                               \
                          (rasterP->height - 1)))                            \
        {                                                                    \
            return ERR_BAD_IMAGE_LAYOUT;                                     \
        }                                                                    \
        lastScanOffset = (elements_per_scan) * (rasterP->height - 1);        \
                                                                             \
        if (!SAFE_TO_ADD(offset, lastScanOffset)) {                          \
            return ERR_BAD_IMAGE_LAYOUT;                                     \
        }                                                                    \
        lastScanOffset += offset;                                            \
                                                                             \
        if (!SAFE_TO_MULT((elements_per_pixel), rasterP->width)) {           \
            return ERR_BAD_IMAGE_LAYOUT;                                     \
        }                                                                    \
        offset = (elements_per_pixel) * rasterP->width;                      \
                                                                             \
        if (!SAFE_TO_ADD(offset, lastScanOffset)) {                          \
            return ERR_BAD_IMAGE_LAYOUT;                                     \
        }                                                                    \
        lastScanOffset += offset;                                            \
                                                                             \
        if (dataArrayLength < lastScanOffset) {                              \
            return ERR_BAD_IMAGE_LAYOUT;                                     \
        }                                                                    \
    } while(0);                                                              \

static int
storeImageArray(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP,
                mlib_image *mlibImP) {
    int mStride;
    unsigned char *cmDataP, *dataP, *cDataP;
    HintS_t *hintP = &dstP->hints;
    RasterS_t *rasterP = &dstP->raster;
    jsize dataArrayLength = (*env)->GetArrayLength(env, rasterP->jdata);
    int y;

    /* REMIND: Store mlib data type? */

    /* Check if it is an IndexColorModel */
    if (dstP->cmodel.cmType == INDEX_CM_TYPE) {
        if (dstP->raster.rasterType == COMPONENT_RASTER_TYPE) {
            return storeICMarray(env, srcP, dstP, mlibImP);
        }
        else {
            /* Packed or some other custom raster */
            cmDataP = (unsigned char *) mlib_ImageGetData(mlibImP);
            return cvtDefaultToCustom(env, dstP, -1, cmDataP);
        }
    }

    if (hintP->packing == BYTE_INTERLEAVED) {
        /* Write it back to the destination */
        if (rasterP->dataType != BYTE_DATA_TYPE) {
            /* We are working with a raster which was marked
               as a byte interleaved due to performance reasons.
               So, we have to convert the length of the data
               array to bytes as well.
            */
            if (!SAFE_TO_MULT(rasterP->dataSize, dataArrayLength)) {
                return ERR_BAD_IMAGE_LAYOUT;
            }
            dataArrayLength *= rasterP->dataSize;
        }

        CHECK_DST_ARRAY(hintP->dataOffset, hintP->sStride, hintP->numChans);
        cmDataP = (unsigned char *) mlib_ImageGetData(mlibImP);
        mStride = mlib_ImageGetStride(mlibImP);
        dataP = (unsigned char *)(*env)->GetPrimitiveArrayCritical(env,
                                                      rasterP->jdata, NULL);
        if (dataP == NULL) return 0;
        cDataP = dataP + hintP->dataOffset;
        for (y=0; y < rasterP->height;
             y++, cmDataP += mStride, cDataP += hintP->sStride)
        {
            memcpy(cDataP, cmDataP, rasterP->width*hintP->numChans);
        }
        (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP,
                                              JNI_ABORT);
    }
    else if (dstP->cmodel.cmType == DIRECT_CM_TYPE) {
        /* Just need to move bits */
        if (mlibImP->type == MLIB_BYTE) {
            if (dstP->hints.packing == PACKED_BYTE_INTER) {
                return setPackedBCRdefault(env, rasterP, -1,
                                           (unsigned char *) mlibImP->data,
                                           dstP->cmodel.supportsAlpha);
            } else if (dstP->hints.packing == PACKED_SHORT_INTER) {
                return setPackedSCRdefault(env, rasterP, -1,
                                           (unsigned char *) mlibImP->data,
                                           dstP->cmodel.supportsAlpha);
            } else if (dstP->hints.packing == PACKED_INT_INTER) {
                return setPackedICRdefault(env, rasterP, -1,
                                           (unsigned char *) mlibImP->data,
                                           dstP->cmodel.supportsAlpha);
            }
        }
        else if (mlibImP->type == MLIB_SHORT) {
            return setPixelsFormMlibImage(env, rasterP, mlibImP);
        }
    }
    else {
        return cvtDefaultToCustom(env, dstP, -1,
                                  (unsigned char *)mlibImP->data);
    }

    return 0;
}

static int
storeRasterArray(JNIEnv *env, RasterS_t *srcP, RasterS_t *dstP,
                mlib_image *mlibImP) {
    unsigned char *cDataP;

    switch(dstP->type) {
    case sun_awt_image_IntegerComponentRaster_TYPE_BYTE_PACKED_SAMPLES:
        cDataP  = (unsigned char *) mlib_ImageGetData(mlibImP);
        return setPackedBCR(env, dstP, -1, cDataP);

    case sun_awt_image_IntegerComponentRaster_TYPE_USHORT_PACKED_SAMPLES:
        if (dstP->sppsm.maxBitSize <= 8) {
            cDataP  = (unsigned char *) mlib_ImageGetData(mlibImP);
            return setPackedSCR(env, dstP, -1, cDataP);
        }
        break;
    case sun_awt_image_IntegerComponentRaster_TYPE_INT_PACKED_SAMPLES:
        if (dstP->sppsm.maxBitSize <= 8) {
            cDataP  = (unsigned char *) mlib_ImageGetData(mlibImP);
            return setPackedICR(env, dstP, -1, cDataP);
        }
    }

    return -1;
}


static int
storeICMarray(JNIEnv *env, BufImageS_t *srcP, BufImageS_t *dstP,
              mlib_image *mlibImP)
{
    int *argb;
    int x, y;
    unsigned char *dataP, *cDataP, *cP;
    unsigned char *sP;
    int aIdx, rIdx, gIdx, bIdx;
    ColorModelS_t *cmodelP = &dstP->cmodel;
    RasterS_t *rasterP = &dstP->raster;

    /* REMIND: Only works for RGB */
    if (cmodelP->csType != java_awt_color_ColorSpace_TYPE_RGB) {
        JNU_ThrowInternalError(env, "Writing to non-RGB images not implemented yet");
        return -1;
    }

    if (srcP->imageType == java_awt_image_BufferedImage_TYPE_INT_ARGB ||
        srcP->imageType == java_awt_image_BufferedImage_TYPE_INT_ARGB_PRE ||
        srcP->imageType == java_awt_image_BufferedImage_TYPE_INT_RGB)
    {
        aIdx = 0;
        rIdx = 1;
        gIdx = 2;
        bIdx = 3;
    }
    else if (srcP->imageType ==java_awt_image_BufferedImage_TYPE_4BYTE_ABGR||
        srcP->imageType == java_awt_image_BufferedImage_TYPE_4BYTE_ABGR_PRE)
    {
        aIdx = 0;
        rIdx = 3;
        gIdx = 2;
        bIdx = 1;
    }
    else if (srcP->imageType == java_awt_image_BufferedImage_TYPE_3BYTE_BGR){
        rIdx = 2;
        gIdx = 1;
        bIdx = 0;
        aIdx = 0;       /* Ignored */
    }
    else if (srcP->cmodel.cmType == INDEX_CM_TYPE) {
        rIdx = 0;
        gIdx = 1;
        bIdx = 2;
        aIdx = 3;   /* Use supportsAlpha to see if it is really there */
    }
    else {
        return -1;
    }

    /* Lock down the destination raster */
    dataP = (unsigned char *) (*env)->GetPrimitiveArrayCritical(env,
                                                  rasterP->jdata, NULL);
    if (dataP == NULL) {
        return -1;
    }
    argb = (*env)->GetPrimitiveArrayCritical(env, cmodelP->jrgb, NULL);
    if (argb == NULL) {
        (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP,
                                              JNI_ABORT);
        return -1;
    }

    cDataP = dataP + dstP->hints.dataOffset;
    sP = (unsigned char *) mlib_ImageGetData(mlibImP);

    for (y=0; y < rasterP->height; y++, cDataP += rasterP->scanlineStride) {
        cP = cDataP;
        for (x=0; x < rasterP->width; x++, cP += rasterP->pixelStride) {
            *cP = colorMatch(sP[rIdx], sP[gIdx], sP[bIdx], sP[aIdx],
                             (unsigned char *)argb, cmodelP->mapSize);
            sP += cmodelP->numComponents;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, cmodelP->jrgb, argb, JNI_ABORT);
    (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata, dataP,
                                          JNI_ABORT);
    return -1;
}

static int expandICM(JNIEnv *env, BufImageS_t *imageP, unsigned int *mDataP)
{
    ColorModelS_t *cmP = &imageP->cmodel;
    RasterS_t *rasterP = &imageP->raster;
    HintS_t *hintP     = &imageP->hints;
    int *rgb;
    int status = 0;
    unsigned char *dataP, *cP;
    unsigned int *mP;
    int width = rasterP->width;
    int height = rasterP->height;
    int x, y;

    /* Need to grab the lookup tables.  Right now only bytes */
    rgb = (int *) (*env)->GetPrimitiveArrayCritical(env, cmP->jrgb, NULL);
    CHECK_NULL_RETURN(rgb, -1);

    /* Interleaved with shared data */
    dataP = (void *) (*env)->GetPrimitiveArrayCritical(env,
                                                       rasterP->jdata, NULL);
    if (dataP == NULL) {
        /* Release the lookup tables */
        (*env)->ReleasePrimitiveArrayCritical(env, cmP->jrgb, rgb, JNI_ABORT);
        return -1;
    }

    if (rasterP->dataType == BYTE_DATA_TYPE) {
        unsigned char *cDataP = ((unsigned char *)dataP) + hintP->dataOffset;

        for (y=0; y < height; y++) {
            mP = mDataP;
            cP = cDataP;
            for (x=0; x < width; x++, cP += rasterP->pixelStride) {
                *mP++ = rgb[*cP];
            }
            mDataP += width;
            cDataP += rasterP->scanlineStride;
        }
    }
    else if (rasterP->dataType == SHORT_DATA_TYPE) {
        unsigned short *sDataP, *sP;
        sDataP = ((unsigned short *)dataP) + hintP->channelOffset;

        for (y=0; y < height; y++) {
            mP = mDataP;
            sP = sDataP;
            for (x=0; x < width; x++, sP+=rasterP->pixelStride) {
                *mP++ = rgb[*sP];
            }
            mDataP += width;
            sDataP += rasterP->scanlineStride;
        }
    }
    else {
        /* Unknown type */
        status = -1;
    }
    /* Release the lookup table data */
    (*env)->ReleasePrimitiveArrayCritical(env, imageP->cmodel.jrgb,
                                          rgb, JNI_ABORT);
    /* Release the data array */
    (*env)->ReleasePrimitiveArrayCritical(env, rasterP->jdata,
                                          dataP, JNI_ABORT);
    return status;
}
/* This routine is expecting a ByteComponentRaster with a PackedColorModel */
static int expandPackedBCR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *outDataP)
{
    int x, y, c;
    unsigned char *outP = outDataP;
    unsigned char *lineInP, *inP;
    jarray jInDataP;
    jint   *inDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jInDataP = (*env)->GetObjectField(env, rasterP->jraster, g_BCRdataID);
    inDataP = (*env)->GetPrimitiveArrayCritical(env, jInDataP, 0);
    if (inDataP == NULL) {
        return -1;
    }
    lineInP =  (unsigned char *)inDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            roff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (roff[c] < 0) {
                loff[c] = -roff[c];
                roff[c] = 0;
            }
            else loff[c] = 0;
        }
        /* Convert the all bands */
        if (rasterP->numBands < 4) {
            /* Need to put in alpha */
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    for (c=0; c < rasterP->numBands; c++) {
                        *outP++ = (unsigned char)
                            (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                             <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    for (c=0; c < rasterP->numBands; c++) {
                        *outP++ = (unsigned char)
                            (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                             <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        roff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (roff[0] < 0) {
            loff[0] = -roff[0];
            roff[0] = 0;
        }
        else loff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            inP = lineInP;
            for (x=0; x < rasterP->width; x++) {
                *outP++ = (unsigned char)
                    ((*inP & rasterP->sppsm.maskArray[c])>>roff[0])<<loff[0];
                inP++;
            }
            lineInP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jInDataP, inDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ByteComponentRaster with a PackedColorModel */
static int expandPackedBCRdefault(JNIEnv *env, RasterS_t *rasterP,
                                  int component, unsigned char *outDataP,
                                  int forceAlpha)
{
    int x, y, c;
    unsigned char *outP = outDataP;
    unsigned char *lineInP, *inP;
    jarray jInDataP;
    jint   *inDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];
    int numBands = rasterP->numBands - (forceAlpha ? 0 : 1);
    int a = numBands;

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jInDataP = (*env)->GetObjectField(env, rasterP->jraster, g_BCRdataID);
    inDataP = (*env)->GetPrimitiveArrayCritical(env, jInDataP, 0);
    if (inDataP == NULL) {
        return -1;
    }
    lineInP =  (unsigned char *)inDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            roff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (roff[c] < 0) {
                loff[c] = -roff[c];
                roff[c] = 0;
            }
            else loff[c] = 0;
        }

        /* Need to put in alpha */
        if (forceAlpha) {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    *outP++ = 0xff;
                    for (c=0; c < numBands; c++) {
                        *outP++ = (unsigned char)
                            (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                             <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    *outP++ = (unsigned char)
                        (((*inP&rasterP->sppsm.maskArray[a]) >> roff[a])
                         <<loff[a]);
                    for (c=0; c < numBands; c++) {
                        *outP++ = (unsigned char)
                            (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                             <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        roff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (roff[0] < 0) {
            loff[0] = -roff[0];
            roff[0] = 0;
        }
        else loff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            inP = lineInP;
            for (x=0; x < rasterP->width; x++) {
                *outP++ = (unsigned char)
                    ((*inP & rasterP->sppsm.maskArray[c])>>roff[0])<<loff[0];
                inP++;
            }
            lineInP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jInDataP, inDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ShortComponentRaster with a PackedColorModel */
static int expandPackedSCR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *outDataP)
{
    int x, y, c;
    unsigned char *outP = outDataP;
    unsigned short *lineInP, *inP;
    jarray jInDataP;
    jint   *inDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jInDataP = (*env)->GetObjectField(env, rasterP->jraster, g_SCRdataID);
    inDataP = (*env)->GetPrimitiveArrayCritical(env, jInDataP, 0);
    if (inDataP == NULL) {
        return -1;
    }
    lineInP =  (unsigned short *)inDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            roff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (roff[c] < 0) {
                loff[c] = -roff[c];
                roff[c] = 0;
            }
            else loff[c] = 0;
        }
        /* Convert the all bands */
        if (rasterP->numBands < 4) {
            /* Need to put in alpha */
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    for (c=0; c < rasterP->numBands; c++) {
                        /*
                         *Not correct.  Might need to unpremult,
                         * shift, etc
                         */
                        *outP++ = (unsigned char)
                            (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                             <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        } else {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    for (c=0; c < rasterP->numBands; c++) {
                        /*
                         *Not correct.  Might need to unpremult,
                         * shift, etc
                         */
                        *outP++ = (unsigned char)
                            (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                             <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        roff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (roff[0] < 0) {
            loff[0] = -roff[0];
            roff[0] = 0;
        }
        else loff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            inP = lineInP;
            for (x=0; x < rasterP->width; x++) {
                *outP++ = (unsigned char)
                    ((*inP & rasterP->sppsm.maskArray[c])>>roff[0])<<loff[0];
                inP++;
            }
            lineInP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jInDataP, inDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ShortComponentRaster with a PackedColorModel */
static int expandPackedSCRdefault(JNIEnv *env, RasterS_t *rasterP,
                                  int component, unsigned char *outDataP,
                                  int forceAlpha)
{
    int x, y, c;
    unsigned char *outP = outDataP;
    unsigned short *lineInP, *inP;
    jarray jInDataP;
    jint   *inDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];
    int numBands = rasterP->numBands - (forceAlpha ? 0 : 1);
    int a = numBands;

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jInDataP = (*env)->GetObjectField(env, rasterP->jraster, g_SCRdataID);
    inDataP = (*env)->GetPrimitiveArrayCritical(env, jInDataP, 0);
    if (inDataP == NULL) {
        return -1;
    }
    lineInP =  (unsigned short *)inDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            roff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (roff[c] < 0) {
                loff[c] = -roff[c];
                roff[c] = 0;
            }
            else loff[c] = 0;
        }

        /* Need to put in alpha */
        if (forceAlpha) {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    *outP++ = 0xff;
                    for (c=0; c < numBands; c++) {
                        /*
                         * Not correct.  Might need to unpremult,
                         * shift, etc
                         */
                        *outP++ = (unsigned char)
                                (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                                   <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    *outP++ = (unsigned char)
                        (((*inP&rasterP->sppsm.maskArray[a]) >> roff[a])
                                   <<loff[a]);
                    for (c=0; c < numBands; c++) {
                        /*
                         * Not correct.  Might need to
                         * unpremult, shift, etc
                         */
                        *outP++ = (unsigned char)
                                (((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                                   <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        roff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (roff[0] < 0) {
            loff[0] = -roff[0];
            roff[0] = 0;
        }
        else loff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            inP = lineInP;
            for (x=0; x < rasterP->width; x++) {
                *outP++ = (unsigned char)
                        ((*inP & rasterP->sppsm.maskArray[c])>>roff[0])<<loff[0];
                inP++;
            }
            lineInP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jInDataP, inDataP, JNI_ABORT);

    return 0;

}

/* This routine is expecting a IntegerComponentRaster with a PackedColorModel*/
static int expandPackedICR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *outDataP)
{
    int x, y, c;
    unsigned char *outP = outDataP;
    unsigned int *lineInP, *inP;
    jarray jInDataP;
    jint   *inDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jInDataP = (*env)->GetObjectField(env, rasterP->jraster, g_ICRdataID);
    inDataP = (*env)->GetPrimitiveArrayCritical(env, jInDataP, 0);
    if (inDataP == NULL) {
        return -1;
    }
    lineInP =  (unsigned int *)inDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            roff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (roff[c] < 0) {
                loff[c] = -roff[c];
                roff[c] = 0;
            }
            else loff[c] = 0;
        }
        /* Convert the all bands */
        if (rasterP->numBands < 4) {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    for (c=0; c < rasterP->numBands; c++) {
                        /*
                         * Not correct.  Might need to unpremult,
                         * shift, etc
                         */
                        *outP++ = (unsigned char)(((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                                   <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    for (c=0; c < rasterP->numBands; c++) {
                        /*
                         * Not correct.  Might need to
                         * unpremult, shift, etc
                         */
                        *outP++ = (unsigned char)(((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                                   <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        roff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (roff[0] < 0) {
            loff[0] = -roff[0];
            roff[0] = 0;
        }
        else loff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            inP = lineInP;
            for (x=0; x < rasterP->width; x++) {
                *outP++ = (unsigned char)(((*inP & rasterP->sppsm.maskArray[c])>>roff[0])<<loff[0]);
                inP++;
            }
            lineInP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jInDataP, inDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a IntegerComponentRaster with a PackedColorModel*/
static int expandPackedICRdefault(JNIEnv *env, RasterS_t *rasterP,
                                  int component, unsigned char *outDataP,
                                  int forceAlpha)
{
    int x, y, c;
    unsigned char *outP = outDataP;
    unsigned int *lineInP, *inP;
    jarray jInDataP;
    jint   *inDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];
    int numBands = rasterP->numBands - (forceAlpha ? 0 : 1);
    int a = numBands;

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jInDataP = (*env)->GetObjectField(env, rasterP->jraster, g_ICRdataID);
    inDataP = (*env)->GetPrimitiveArrayCritical(env, jInDataP, 0);
    if (inDataP == NULL) {
        return -1;
    }
    lineInP =  (unsigned int *)inDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            roff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (roff[c] < 0) {
                loff[c] = -roff[c];
                roff[c] = 0;
            }
            else loff[c] = 0;
        }

        /* Need to put in alpha */
        if (forceAlpha) {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    *outP++ = 0xff;
                    for (c=0; c < numBands; c++) {
                        /*
                         * Not correct.  Might need to unpremult,
                         * shift, etc
                         */
                        *outP++ = (unsigned char)(((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                                   <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                inP = lineInP;
                for (x=0; x < rasterP->width; x++) {
                    *outP++ = (unsigned char)(((*inP&rasterP->sppsm.maskArray[a]) >> roff[a])
                                   <<loff[a]);
                    for (c=0; c < numBands; c++) {
                        /*
                         * Not correct.  Might need to
                         * unpremult, shift, etc
                         */
                        *outP++ = (unsigned char)(((*inP&rasterP->sppsm.maskArray[c]) >> roff[c])
                                   <<loff[c]);
                    }
                    inP++;
                }
                lineInP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        roff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (roff[0] < 0) {
            loff[0] = -roff[0];
            roff[0] = 0;
        }
        else loff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            inP = lineInP;
            for (x=0; x < rasterP->width; x++) {
                *outP++ = (unsigned char)(((*inP & rasterP->sppsm.maskArray[c])>>roff[0])<<loff[0]);
                inP++;
            }
            lineInP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jInDataP, inDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ByteComponentRaster with a PackedColorModel */
static int setPackedBCR(JNIEnv *env, RasterS_t *rasterP, int component,
                        unsigned char *inDataP)
{
    int x, y, c;
    unsigned char *inP = inDataP;
    unsigned char *lineOutP, *outP;
    jarray jOutDataP;
    jsize dataArrayLength;
    unsigned char *outDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jOutDataP = (*env)->GetObjectField(env, rasterP->jraster, g_BCRdataID);
    if (JNU_IsNull(env, jOutDataP)) {
        return -1;
    }

    dataArrayLength = (*env)->GetArrayLength(env, jOutDataP);
    CHECK_DST_ARRAY(rasterP->chanOffsets[0], rasterP->scanlineStride, 1);

    outDataP = (*env)->GetPrimitiveArrayCritical(env, jOutDataP, 0);
    if (outDataP == NULL) {
        return -1;
    }
    lineOutP = outDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            loff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (loff[c] < 0) {
                roff[c] = -loff[c];
                loff[c] = 0;
            }
            else roff[c] = 0;
        }
        /* Convert the all bands */
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            *outP = 0;
            for (x=0; x < rasterP->width; x++) {
                for (c=0; c < rasterP->numBands; c++, inP++) {
                    *outP |= (*inP<<loff[c]>>roff[c])&rasterP->sppsm.maskArray[c];
                }
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }
    else {
        c = component;
        loff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (loff[0] < 0) {
            roff[0] = -loff[0];
            loff[0] = 0;
        }
        else roff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++, inP++) {
                *outP |= (*inP<<loff[0]>>roff[0])&rasterP->sppsm.maskArray[c];
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jOutDataP, outDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ShortComponentRaster with a PackedColorModel */
static int setPackedSCR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *inDataP)
{
    int x, y, c;
    unsigned char *inP = inDataP;
    unsigned short *lineOutP, *outP;
    jarray jOutDataP;
    jsize dataArrayLength;
    unsigned short *outDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jOutDataP = (*env)->GetObjectField(env, rasterP->jraster, g_SCRdataID);
    if (JNU_IsNull(env, jOutDataP)) {
        return -1;
    }

    dataArrayLength = (*env)->GetArrayLength(env, jOutDataP);
    CHECK_DST_ARRAY(rasterP->chanOffsets[0], rasterP->scanlineStride, 1);

    outDataP = (*env)->GetPrimitiveArrayCritical(env, jOutDataP, 0);
    if (outDataP == NULL) {
        return -1;
    }
    lineOutP = outDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            loff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (loff[c] < 0) {
                roff[c] = -loff[c];
                loff[c] = 0;
            }
            else roff[c] = 0;
        }
        /* Convert the all bands */
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++) {
                for (c=0; c < rasterP->numBands; c++, inP++) {
                    /* Not correct.  Might need to unpremult, shift, etc */
                    *outP |= (*inP<<loff[c]>>roff[c])&rasterP->sppsm.maskArray[c];
                }
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }
    else {
        c = component;
        loff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (loff[0] < 0) {
            roff[0] = -loff[0];
            loff[0] = 0;
        }
        else roff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++, inP++) {
                *outP |= (*inP<<loff[0]>>roff[0])&rasterP->sppsm.maskArray[c];
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jOutDataP, outDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a IntegerComponentRaster with a PackedColorModel*/
static int setPackedICR(JNIEnv *env, RasterS_t *rasterP, int component,
                           unsigned char *inDataP)
{
    int x, y, c;
    unsigned char *inP = inDataP;
    unsigned int *lineOutP, *outP;
    jarray jOutDataP;
    jsize dataArrayLength;
    unsigned int *outDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jOutDataP = (*env)->GetObjectField(env, rasterP->jraster, g_ICRdataID);
    if (JNU_IsNull(env, jOutDataP)) {
        return -1;
    }

    dataArrayLength = (*env)->GetArrayLength(env, jOutDataP);
    CHECK_DST_ARRAY(rasterP->chanOffsets[0], rasterP->scanlineStride, 1);

    outDataP = (*env)->GetPrimitiveArrayCritical(env, jOutDataP, 0);
    if (outDataP == NULL) {
        return -1;
    }
    lineOutP = outDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            loff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (loff[c] < 0) {
                roff[c] = -loff[c];
                loff[c] = 0;
            }
            else roff[c] = 0;
        }
        /* Convert the all bands */
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++) {
                for (c=0; c < rasterP->numBands; c++, inP++) {
                    /* Not correct.  Might need to unpremult, shift, etc */
                    *outP |= (*inP<<loff[c]>>roff[c])&rasterP->sppsm.maskArray[c];
                }
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }
    else {
        c = component;
        loff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (loff[0] < 0) {
            roff[0] = -loff[0];
            loff[0] = 0;
        }
        else roff[c] = 0;

        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++, inP++) {
                *outP |= (*inP<<loff[0]>>roff[0])&rasterP->sppsm.maskArray[c];
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jOutDataP, outDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ByteComponentRaster with a PackedColorModel */
static int setPackedBCRdefault(JNIEnv *env, RasterS_t *rasterP,
                               int component, unsigned char *inDataP,
                               int supportsAlpha)
{
    int x, y, c;
    unsigned char *inP = inDataP;
    unsigned char *lineOutP, *outP;
    jarray jOutDataP;
    jsize  dataArrayLength;
    unsigned char *outDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];
    int a = rasterP->numBands - 1;

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jOutDataP = (*env)->GetObjectField(env, rasterP->jraster, g_BCRdataID);
    if (JNU_IsNull(env, jOutDataP)) {
        return -1;
    }

    dataArrayLength = (*env)->GetArrayLength(env, jOutDataP);
    CHECK_DST_ARRAY(rasterP->chanOffsets[0], rasterP->scanlineStride, 1);

    outDataP = (*env)->GetPrimitiveArrayCritical(env, jOutDataP, 0);
    if (outDataP == NULL) {
        return -1;
    }
    lineOutP = outDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            loff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (loff[c] < 0) {
                roff[c] = -loff[c];
                loff[c] = 0;
            }
            else roff[c] = 0;
        }
        /* Convert the all bands */
        if (supportsAlpha) {
            for (y=0; y < rasterP->height; y++) {
                outP = lineOutP;
                *outP = 0;
                for (x=0; x < rasterP->width; x++) {
                    *outP |= (*inP<<loff[a]>>roff[a])&
                        rasterP->sppsm.maskArray[a];
                    inP++;
                    for (c=0; c < rasterP->numBands-1; c++, inP++) {
                        *outP |= (*inP<<loff[c]>>roff[c])&
                            rasterP->sppsm.maskArray[c];
                    }
                    outP++;
                }
                lineOutP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                outP = lineOutP;
                *outP = 0;
                for (x=0; x < rasterP->width; x++) {
                    inP++;
                    for (c=0; c < rasterP->numBands; c++, inP++) {
                        *outP |= (*inP<<loff[c]>>roff[c])&rasterP->sppsm.maskArray[c];
                    }
                    outP++;
                }
                lineOutP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        loff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (loff[0] < 0) {
            roff[0] = -loff[0];
            loff[0] = 0;
        }
        else roff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++, inP++) {
                *outP |= (*inP<<loff[0]>>roff[0])&rasterP->sppsm.maskArray[c];
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jOutDataP, outDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a ShortComponentRaster with a PackedColorModel */
static int setPackedSCRdefault(JNIEnv *env, RasterS_t *rasterP,
                               int component, unsigned char *inDataP,
                               int supportsAlpha)
{
    int x, y, c;
    unsigned char *inP = inDataP;
    unsigned short *lineOutP, *outP;
    jarray jOutDataP;
    jsize dataArrayLength;
    unsigned short *outDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];
    int a = rasterP->numBands - 1;

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jOutDataP = (*env)->GetObjectField(env, rasterP->jraster, g_SCRdataID);
    if (JNU_IsNull(env, jOutDataP)) {
        return -1;
    }
    dataArrayLength = (*env)->GetArrayLength(env, jOutDataP);
    CHECK_DST_ARRAY(rasterP->chanOffsets[0], rasterP->scanlineStride, 1);

    outDataP = (*env)->GetPrimitiveArrayCritical(env, jOutDataP, 0);
    if (outDataP == NULL) {
        return -1;
    }
    lineOutP = outDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            loff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (loff[c] < 0) {
                roff[c] = -loff[c];
                loff[c] = 0;
            }
            else roff[c] = 0;
        }
        /* Convert the all bands */
        if (supportsAlpha) {
            for (y=0; y < rasterP->height; y++) {
                outP = lineOutP;
                for (x=0; x < rasterP->width; x++) {
                    *outP |= (*inP<<loff[a]>>roff[a])&
                        rasterP->sppsm.maskArray[a];
                    inP++;
                    for (c=0; c < rasterP->numBands-1; c++, inP++) {
                        /* Not correct.  Might need to unpremult, shift, etc */
                        *outP |= (*inP<<loff[c]>>roff[c])&
                            rasterP->sppsm.maskArray[c];
                    }
                    outP++;
                }
                lineOutP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                outP = lineOutP;
                for (x=0; x < rasterP->width; x++) {
                    inP++;
                    for (c=0; c < rasterP->numBands; c++, inP++) {
                        /* Not correct.  Might need to unpremult, shift, etc */
                        *outP |= (*inP<<loff[c]>>roff[c])&rasterP->sppsm.maskArray[c];
                    }
                    outP++;
                }
                lineOutP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        loff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (loff[0] < 0) {
            roff[0] = -loff[0];
            loff[0] = 0;
        }
        else roff[c] = 0;
        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++, inP++) {
                *outP |= (*inP<<loff[0]>>roff[0])&rasterP->sppsm.maskArray[c];
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jOutDataP, outDataP, JNI_ABORT);

    return 0;
}

/* This routine is expecting a IntegerComponentRaster with a PackedColorModel*/
static int setPackedICRdefault(JNIEnv *env, RasterS_t *rasterP,
                               int component, unsigned char *inDataP,
                               int supportsAlpha)
{
    int x, y, c;
    unsigned char *inP = inDataP;
    unsigned int *lineOutP, *outP;
    jarray jOutDataP;
    jsize dataArrayLength;
    unsigned int *outDataP;
    int loff[MAX_NUMBANDS], roff[MAX_NUMBANDS];
    int a = rasterP->numBands - 1;

    if (rasterP->numBands > MAX_NUMBANDS) {
        return -1;
    }

    /* Grab data ptr, strides, offsets from raster */
    jOutDataP = (*env)->GetObjectField(env, rasterP->jraster, g_ICRdataID);
    if (JNU_IsNull(env, jOutDataP)) {
        return -1;
    }

    dataArrayLength = (*env)->GetArrayLength(env, jOutDataP);
    CHECK_DST_ARRAY(rasterP->chanOffsets[0], rasterP->scanlineStride, 1);

    outDataP = (*env)->GetPrimitiveArrayCritical(env, jOutDataP, 0);
    if (outDataP == NULL) {
        return -1;
    }
    lineOutP = outDataP + rasterP->chanOffsets[0];

    if (component < 0) {
        for (c=0; c < rasterP->numBands; c++) {
            loff[c] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
            if (loff[c] < 0) {
                roff[c] = -loff[c];
                loff[c] = 0;
            }
            else roff[c] = 0;
        }
        /* Convert the all bands */
        if (supportsAlpha) {
            for (y=0; y < rasterP->height; y++) {
                outP = lineOutP;
                for (x=0; x < rasterP->width; x++) {
                    *outP |= (*inP<<loff[a]>>roff[a])&
                        rasterP->sppsm.maskArray[a];
                    inP++;
                    for (c=0; c < rasterP->numBands-1; c++, inP++) {
                        /* Not correct.  Might need to unpremult, shift, etc */
                        *outP |= (*inP<<loff[c]>>roff[c])&
                            rasterP->sppsm.maskArray[c];
                    }
                    outP++;
                }
                lineOutP += rasterP->scanlineStride;
            }
        }
        else {
            for (y=0; y < rasterP->height; y++) {
                outP = lineOutP;
                for (x=0; x < rasterP->width; x++) {
                    inP++;
                    for (c=0; c < rasterP->numBands; c++, inP++) {
                        /* Not correct.  Might need to unpremult, shift, etc */
                        *outP |= (*inP<<loff[c]>>roff[c])&
                            rasterP->sppsm.maskArray[c];
                    }
                    outP++;
                }
                lineOutP += rasterP->scanlineStride;
            }
        }
    }
    else {
        c = component;
        loff[0] = rasterP->sppsm.offsets[c] + (rasterP->sppsm.nBits[c]-8);
        if (loff[0] < 0) {
            roff[0] = -loff[0];
            loff[0] = 0;
        }
        else roff[c] = 0;

        for (y=0; y < rasterP->height; y++) {
            outP = lineOutP;
            for (x=0; x < rasterP->width; x++, inP++) {
                *outP |= (*inP<<loff[0]>>roff[0])&rasterP->sppsm.maskArray[c];
                outP++;
            }
            lineOutP += rasterP->scanlineStride;
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, jOutDataP, outDataP, JNI_ABORT);

    return 0;
}

/* This is temporary code.  Should go away when there is better color
 * conversion code available.
 * REMIND:  Ignoring alpha
 */
/* returns the absolute value x */
#define ABS(x) ((x) < 0 ? -(x) : (x))
#define CLIP(val,min,max)       ((val < min) ? min : ((val > max) ? max : val))

static int
colorMatch(int r, int g, int b, int a, unsigned char *argb, int numColors) {
    int besti = 0;
    int mindist, i, t, d;
    unsigned char red, green, blue;

    r = CLIP(r, 0, 255);
    g = CLIP(g, 0, 255);
    b = CLIP(b, 0, 255);

    /* look for pure gray match */
    if ((r == g) && (g == b)) {
        mindist = 256;
        for (i = 0 ; i < numColors ; i++, argb+=4) {
            red = argb[1];
            green = argb[2];
            blue = argb[3];
            if (! ((red == green) && (green == blue)) ) {
                continue;
            }
            d = ABS(red - r);
            if (d == 0)
                return i;
            if (d < mindist) {
                besti = i;
                mindist = d;
            }
        }
        return besti;
    }

    /* look for non-pure gray match */
    mindist = 256 * 256 * 256;
    for (i = 0 ; i < numColors ; i++, argb+=4) {
        red = argb[1];
        green = argb[2];
        blue = argb[3];
        t = red - r;
        d = t * t;
        if (d >= mindist) {
            continue;
        }
        t = green - g;
        d += t * t;
        if (d >= mindist) {
            continue;
        }
        t = blue - b;
        d += t * t;
        if (d >= mindist) {
            continue;
        }
        if (d == 0)
            return i;
        if (d < mindist) {
            besti = i;
            mindist = d;
        }
    }

    return besti;
}
