FileDocCategorySizeDatePackage
ColorConvertOp.javaAPI DocJava SE 6 API44567Tue Jun 10 00:25:26 BST 2008java.awt.image

ColorConvertOp

public class ColorConvertOp extends Object implements BufferedImageOp, RasterOp
This class performs a pixel-by-pixel color conversion of the data in the source image. The resulting color values are scaled to the precision of the destination image. Color conversion can be specified via an array of ColorSpace objects or an array of ICC_Profile objects.

If the source is a BufferedImage with premultiplied alpha, the color components are divided by the alpha component before color conversion. If the destination is a BufferedImage with premultiplied alpha, the color components are multiplied by the alpha component after conversion. Rasters are treated as having no alpha channel, i.e. all bands are color bands.

If a RenderingHints object is specified in the constructor, the color rendering hint and the dithering hint may be used to control color conversion.

Note that Source and Destination may be the same object.

see
java.awt.RenderingHints#KEY_COLOR_RENDERING
see
java.awt.RenderingHints#KEY_DITHERING

Fields Summary
ICC_Profile[]
profileList
ColorSpace[]
CSList
sun.awt.color.ICC_Transform
thisTransform
sun.awt.color.ICC_Transform
thisRasterTransform
ICC_Profile
thisSrcProfile
ICC_Profile
thisDestProfile
RenderingHints
hints
boolean
gotProfiles
float[]
srcMinVals
float[]
srcMaxVals
float[]
dstMinVals
float[]
dstMaxVals
Constructors Summary
public ColorConvertOp(RenderingHints hints)
Constructs a new ColorConvertOp which will convert from a source color space to a destination color space. The RenderingHints argument may be null. This Op can be used only with BufferedImages, and will convert directly from the ColorSpace of the source image to that of the destination. The destination argument of the filter method cannot be specified as null.

param
hints the RenderingHints object used to control the color conversion, or null

        if (ProfileDeferralMgr.deferring) {
            ProfileDeferralMgr.activateProfiles();
        }
    
        profileList = new ICC_Profile [0];    /* 0 length list */
        this.hints  = hints;
    
public ColorConvertOp(ColorSpace cspace, RenderingHints hints)
Constructs a new ColorConvertOp from a ColorSpace object. The RenderingHints argument may be null. This Op can be used only with BufferedImages, and is primarily useful when the {@link #filter(BufferedImage, BufferedImage) filter} method is invoked with a destination argument of null. In that case, the ColorSpace defines the destination color space for the destination created by the filter method. Otherwise, the ColorSpace defines an intermediate space to which the source is converted before being converted to the destination space.

param
cspace defines the destination ColorSpace or an intermediate ColorSpace
param
hints the RenderingHints object used to control the color conversion, or null
throws
NullPointerException if cspace is null

        if (cspace == null) {
            throw new NullPointerException("ColorSpace cannot be null");
        }
        if (cspace instanceof ICC_ColorSpace) {
            profileList = new ICC_Profile [1];    /* 1 profile in the list */

            profileList [0] = ((ICC_ColorSpace) cspace).getProfile();
        }
        else {
            CSList = new ColorSpace[1]; /* non-ICC case: 1 ColorSpace in list */
            CSList[0] = cspace;
        }
        this.hints  = hints;
    
public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, RenderingHints hints)
Constructs a new ColorConvertOp from two ColorSpace objects. The RenderingHints argument may be null. This Op is primarily useful for calling the filter method on Rasters, in which case the two ColorSpaces define the operation to be performed on the Rasters. In that case, the number of bands in the source Raster must match the number of components in srcCspace, and the number of bands in the destination Raster must match the number of components in dstCspace. For BufferedImages, the two ColorSpaces define intermediate spaces through which the source is converted before being converted to the destination space.

param
srcCspace the source ColorSpace
param
dstCspace the destination ColorSpace
param
hints the RenderingHints object used to control the color conversion, or null
throws
NullPointerException if either srcCspace or dstCspace is null

        if ((srcCspace == null) || (dstCspace == null)) {
            throw new NullPointerException("ColorSpaces cannot be null");
        }
        if ((srcCspace instanceof ICC_ColorSpace) &&
            (dstCspace instanceof ICC_ColorSpace)) {
            profileList = new ICC_Profile [2];    /* 2 profiles in the list */

            profileList [0] = ((ICC_ColorSpace) srcCspace).getProfile();
            profileList [1] = ((ICC_ColorSpace) dstCspace).getProfile();

            getMinMaxValsFromColorSpaces(srcCspace, dstCspace);
        } else {
            /* non-ICC case: 2 ColorSpaces in list */
            CSList = new ColorSpace[2];
            CSList[0] = srcCspace;
            CSList[1] = dstCspace;
        }
        this.hints  = hints;
    
public ColorConvertOp(ICC_Profile[] profiles, RenderingHints hints)
Constructs a new ColorConvertOp from an array of ICC_Profiles. The RenderingHints argument may be null. The sequence of profiles may include profiles that represent color spaces, profiles that represent effects, etc. If the whole sequence does not represent a well-defined color conversion, an exception is thrown.

For BufferedImages, if the ColorSpace of the source BufferedImage does not match the requirements of the first profile in the array, the first conversion is to an appropriate ColorSpace. If the requirements of the last profile in the array are not met by the ColorSpace of the destination BufferedImage, the last conversion is to the destination's ColorSpace.

For Rasters, the number of bands in the source Raster must match the requirements of the first profile in the array, and the number of bands in the destination Raster must match the requirements of the last profile in the array. The array must have at least two elements or calling the filter method for Rasters will throw an IllegalArgumentException.

param
profiles the array of ICC_Profile objects
param
hints the RenderingHints object used to control the color conversion, or null
exception
IllegalArgumentException when the profile sequence does not specify a well-defined color conversion
exception
NullPointerException if profiles is null

        if (profiles == null) {
            throw new NullPointerException("Profiles cannot be null");
        }
        gotProfiles = true;
        profileList = new ICC_Profile[profiles.length];
        for (int i1 = 0; i1 < profiles.length; i1++) {
	    profileList[i1] = profiles[i1];
	}
        this.hints  = hints;
    
Methods Summary
private final java.awt.image.BufferedImageICCBIFilter(java.awt.image.BufferedImage src, java.awt.color.ColorSpace srcColorSpace, java.awt.image.BufferedImage dest, java.awt.color.ColorSpace destColorSpace)

    int              nProfiles = profileList.length;
    ICC_Profile      srcProfile = null, destProfile = null;

        srcProfile = ((ICC_ColorSpace) srcColorSpace).getProfile();

        if (dest == null) {        /* last profile in the list defines
                                      the output color space */
            if (nProfiles == 0) {
                throw new IllegalArgumentException(
                    "Destination ColorSpace is undefined");
            }
            destProfile = profileList [nProfiles - 1];
            dest = createCompatibleDestImage(src, null);
        }
        else {
            if (src.getHeight() != dest.getHeight() ||
                src.getWidth() != dest.getWidth()) {
                throw new IllegalArgumentException(
                    "Width or height of BufferedImages do not match");
            }
            destProfile = ((ICC_ColorSpace) destColorSpace).getProfile();
        }

        /* Checking if all profiles in the transform sequence are the same.
         * If so, performing just copying the data. 
         */
        if (srcProfile == destProfile) {
            boolean noTrans = true;
            for (int i = 0; i < nProfiles; i++) {
                if (srcProfile != profileList[i]) {
                    noTrans = false;
                    break;
                }
            }
            if (noTrans) {
                Graphics2D g = dest.createGraphics();
                try {
                    g.drawImage(src, 0, 0, null);
                } finally {
                    g.dispose();
                }

                return dest;
            }
        }
 
        /* make a new transform if needed */
        if ((thisTransform == null) || (thisSrcProfile != srcProfile) ||
            (thisDestProfile != destProfile) ) {
            updateBITransform(srcProfile, destProfile);
        }

        /* color convert the image */
        thisTransform.colorConvert(src, dest);

        return dest;
    
public java.awt.image.BufferedImagecreateCompatibleDestImage(java.awt.image.BufferedImage src, java.awt.image.ColorModel destCM)
Creates a zeroed destination image with the correct size and number of bands, given this source.

param
src Source image for the filter operation.
param
destCM ColorModel of the destination. If null, an appropriate ColorModel will be used.
return
a BufferedImage with the correct size and number of bands from the specified src.
throws
IllegalArgumentException if destCM is null and this ColorConvertOp was created without any ICC_Profile or ColorSpace defined for the destination

        ColorSpace cs = null;;
        if (destCM == null) {
            if (CSList == null) {
                /* ICC case */
                int nProfiles = profileList.length;
                if (nProfiles == 0) {
                    throw new IllegalArgumentException(
                        "Destination ColorSpace is undefined");
                }
                ICC_Profile destProfile = profileList[nProfiles - 1];
                cs = new ICC_ColorSpace(destProfile);
            } else {
                /* non-ICC case */
                int nSpaces = CSList.length;
                cs = CSList[nSpaces - 1];
            }
        }
        return createCompatibleDestImage(src, destCM, cs);
    
private java.awt.image.BufferedImagecreateCompatibleDestImage(java.awt.image.BufferedImage src, java.awt.image.ColorModel destCM, java.awt.color.ColorSpace destCS)

        BufferedImage image;
        if (destCM == null) {
            ColorModel srcCM = src.getColorModel();
            int nbands = destCS.getNumComponents();
            boolean hasAlpha = srcCM.hasAlpha();
            if (hasAlpha) {
               nbands += 1;
            }
            int[] nbits = new int[nbands];
            for (int i = 0; i < nbands; i++) {
                nbits[i] = 8;
            }
            destCM = new ComponentColorModel(destCS, nbits, hasAlpha,
                                             srcCM.isAlphaPremultiplied(),
                                             srcCM.getTransparency(),
                                             DataBuffer.TYPE_BYTE);
        }
        int w = src.getWidth();
        int h = src.getHeight();
        image = new BufferedImage(destCM,
                                  destCM.createCompatibleWritableRaster(w, h),
                                  destCM.isAlphaPremultiplied(), null);
        return image;
    
public java.awt.image.WritableRastercreateCompatibleDestRaster(java.awt.image.Raster src)
Creates a zeroed destination Raster with the correct size and number of bands, given this source.

param
src the specified Raster
return
a WritableRaster with the correct size and number of bands from the specified src
throws
IllegalArgumentException if this ColorConvertOp was created without sufficient information to define the dst and src color spaces

        int ncomponents;

        if (CSList != null) {
            /* non-ICC case */
            if (CSList.length != 2) {
                throw new IllegalArgumentException(
                    "Destination ColorSpace is undefined");
            }
            ncomponents = CSList[1].getNumComponents();
        } else {
            /* ICC case */
            int nProfiles = profileList.length;
            if (nProfiles < 2) {
                throw new IllegalArgumentException(
                    "Destination ColorSpace is undefined");
            }
            ncomponents = profileList[nProfiles-1].getNumComponents();
        }

        WritableRaster dest =
            Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
                                  src.getWidth(),
                                  src.getHeight(),
                                  ncomponents,
                                  new Point(src.getMinX(), src.getMinY()));
        return dest;
    
public final java.awt.image.BufferedImagefilter(java.awt.image.BufferedImage src, java.awt.image.BufferedImage dest)
ColorConverts the source BufferedImage. If the destination image is null, a BufferedImage will be created with an appropriate ColorModel.

param
src the source BufferedImage to be converted
param
dest the destination BufferedImage, or null
return
dest color converted from src or a new, converted BufferedImage if dest is null
exception
IllegalArgumentException if dest is null and this op was constructed using the constructor which takes only a RenderingHints argument, since the operation is ill defined.

        ColorSpace srcColorSpace, destColorSpace;
        BufferedImage savdest = null;

        if (src.getColorModel() instanceof IndexColorModel) {
            IndexColorModel icm = (IndexColorModel) src.getColorModel();
            src = icm.convertToIntDiscrete(src.getRaster(), true);
        }
        srcColorSpace = src.getColorModel().getColorSpace();
        if (dest != null) {
            if (dest.getColorModel() instanceof IndexColorModel) {
                savdest = dest;
                dest = null;
                destColorSpace = null;
            } else {
                destColorSpace = dest.getColorModel().getColorSpace();
            }
        } else {
            destColorSpace = null;
        }

        if ((CSList != null) ||
            (!(srcColorSpace instanceof ICC_ColorSpace)) ||
            ((dest != null) &&
             (!(destColorSpace instanceof ICC_ColorSpace)))) {
            /* non-ICC case */
            dest = nonICCBIFilter(src, srcColorSpace, dest, destColorSpace);
        } else {
            dest = ICCBIFilter(src, srcColorSpace, dest, destColorSpace);
        }

        if (savdest != null) {
            Graphics2D big = savdest.createGraphics();
	    try {
	        big.drawImage(dest, 0, 0, null);
	    } finally {
	        big.dispose();
	    }
            return savdest;
        } else {
            return dest;
        }
    
public final java.awt.image.WritableRasterfilter(java.awt.image.Raster src, java.awt.image.WritableRaster dest)
ColorConverts the image data in the source Raster. If the destination Raster is null, a new Raster will be created. The number of bands in the source and destination Rasters must meet the requirements explained above. The constructor used to create this ColorConvertOp must have provided enough information to define both source and destination color spaces. See above. Otherwise, an exception is thrown.

param
src the source Raster to be converted
param
dest the destination WritableRaster, or null
return
dest color converted from src or a new, converted WritableRaster if dest is null
exception
IllegalArgumentException if the number of source or destination bands is incorrect, the source or destination color spaces are undefined, or this op was constructed with one of the constructors that applies only to operations on BufferedImages.


        if (CSList != null) {
            /* non-ICC case */
            return nonICCRasterFilter(src, dest);
        }
        int nProfiles = profileList.length;
        if (nProfiles < 2) {
            throw new IllegalArgumentException(
                "Source or Destination ColorSpace is undefined");
        }
        if (src.getNumBands() != profileList[0].getNumComponents()) {
            throw new IllegalArgumentException(
                "Numbers of source Raster bands and source color space " +
                "components do not match");
        }
        if (dest == null) {
            dest = createCompatibleDestRaster(src);
        }
        else {
            if (src.getHeight() != dest.getHeight() ||
                src.getWidth() != dest.getWidth()) {
                throw new IllegalArgumentException(
                    "Width or height of Rasters do not match");
            }
            if (dest.getNumBands() !=
                profileList[nProfiles-1].getNumComponents()) {
                throw new IllegalArgumentException(
                    "Numbers of destination Raster bands and destination " +
                    "color space components do not match");
            }
        }

        /* make a new transform if needed */
        if (thisRasterTransform == null) {
            int              i1, whichTrans, renderState;
            ICC_Transform[]  theTransforms;

            /* make the transform list */
            theTransforms = new ICC_Transform [nProfiles];

            /* initialize transform get loop */
            if (profileList[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) {
                                            /* if first profile is a printer
                                               render as colorimetric */
                renderState = ICC_Profile.icRelativeColorimetric;
            }
            else {
                renderState = ICC_Profile.icPerceptual; /* render any other
                                                           class perceptually */
            }

            whichTrans = ICC_Transform.In;

            /* get the transforms from each profile */
            for (i1 = 0; i1 < nProfiles; i1++) {
                if (i1 == nProfiles -1) {         /* last profile? */
                    whichTrans = ICC_Transform.Out; /* get output transform */
                }
                else {	/* check for abstract profile */
                    if ((whichTrans == ICC_Transform.Simulation) &&
                        (profileList[i1].getProfileClass () ==
                         ICC_Profile.CLASS_ABSTRACT)) {
                	renderState = ICC_Profile.icPerceptual;
                        whichTrans = ICC_Transform.In;
                    }
                }

                theTransforms[i1] = new ICC_Transform (profileList[i1],
                                                       renderState, whichTrans);

                /* get this profile's rendering intent to select transform
                   from next profile */
                renderState = getRenderingIntent(profileList[i1]);

                /* "middle" profiles use simulation transform */
                whichTrans = ICC_Transform.Simulation;
            }

            /* make the net transform */
            thisRasterTransform = new ICC_Transform (theTransforms);
        }

        int srcTransferType = src.getTransferType();
        int dstTransferType = dest.getTransferType();
        if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
            (srcTransferType == DataBuffer.TYPE_DOUBLE) ||
            (dstTransferType == DataBuffer.TYPE_FLOAT) ||
            (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
            if (srcMinVals == null) {
                getMinMaxValsFromProfiles(profileList[0],
                                          profileList[nProfiles-1]);
            }
            /* color convert the raster */
            thisRasterTransform.colorConvert(src, dest,
                                             srcMinVals, srcMaxVals,
                                             dstMinVals, dstMaxVals);
        } else {
            /* color convert the raster */
            thisRasterTransform.colorConvert(src, dest);
        }


        return dest;
    
public final java.awt.geom.Rectangle2DgetBounds2D(java.awt.image.BufferedImage src)
Returns the bounding box of the destination, given this source. Note that this will be the same as the the bounding box of the source.

param
src the source BufferedImage
return
a Rectangle2D that is the bounding box of the destination, given the specified src

        return getBounds2D(src.getRaster());
    
public final java.awt.geom.Rectangle2DgetBounds2D(java.awt.image.Raster src)
Returns the bounding box of the destination, given this source. Note that this will be the same as the the bounding box of the source.

param
src the source Raster
return
a Rectangle2D that is the bounding box of the destination, given the specified src

        /*        return new Rectangle (src.getXOffset(),
                              src.getYOffset(),
                              src.getWidth(), src.getHeight()); */
        return src.getBounds();
    
public final java.awt.color.ICC_Profile[]getICC_Profiles()
Returns the array of ICC_Profiles used to construct this ColorConvertOp. Returns null if the ColorConvertOp was not constructed from such an array.

return
the array of ICC_Profile objects of this ColorConvertOp, or null if this ColorConvertOp was not constructed with an array of ICC_Profile objects.

        if (gotProfiles) {
            ICC_Profile[] profiles = new ICC_Profile[profileList.length];
            for (int i1 = 0; i1 < profileList.length; i1++) {
                profiles[i1] = profileList[i1];
            }
            return profiles;
        }
        return null;
    
private voidgetMinMaxValsFromColorSpaces(java.awt.color.ColorSpace srcCspace, java.awt.color.ColorSpace dstCspace)

        int nc = srcCspace.getNumComponents();
        srcMinVals = new float[nc];
        srcMaxVals = new float[nc];
        for (int i = 0; i < nc; i++) {
            srcMinVals[i] = srcCspace.getMinValue(i);
            srcMaxVals[i] = srcCspace.getMaxValue(i);
        }
        nc = dstCspace.getNumComponents();
        dstMinVals = new float[nc];
        dstMaxVals = new float[nc];
        for (int i = 0; i < nc; i++) {
            dstMinVals[i] = dstCspace.getMinValue(i);
            dstMaxVals[i] = dstCspace.getMaxValue(i);
        }
    
private voidgetMinMaxValsFromProfiles(java.awt.color.ICC_Profile srcProfile, java.awt.color.ICC_Profile dstProfile)

        int type = srcProfile.getColorSpaceType();
        int nc = srcProfile.getNumComponents();
        srcMinVals = new float[nc];
        srcMaxVals = new float[nc];
        setMinMax(type, nc, srcMinVals, srcMaxVals);
        type = dstProfile.getColorSpaceType();
        nc = dstProfile.getNumComponents();
        dstMinVals = new float[nc];
        dstMaxVals = new float[nc];
        setMinMax(type, nc, dstMinVals, dstMaxVals);
    
public final java.awt.geom.Point2DgetPoint2D(java.awt.geom.Point2D srcPt, java.awt.geom.Point2D dstPt)
Returns the location of the destination point given a point in the source. If dstPt is non-null, it will be used to hold the return value. Note that for this class, the destination point will be the same as the source point.

param
srcPt the specified source Point2D
param
dstPt the destination Point2D
return
dstPt after setting its location to be the same as srcPt

        if (dstPt == null) {
            dstPt = new Point2D.Float();
        }
        dstPt.setLocation(srcPt.getX(), srcPt.getY());

        return dstPt;
    
public final java.awt.RenderingHintsgetRenderingHints()
Returns the rendering hints used by this op.

return
the RenderingHints object of this ColorConvertOp

        return hints;
    
private intgetRenderingIntent(java.awt.color.ICC_Profile profile)
Returns the RenderingIntent from the specified ICC Profile.

        byte[] header = profile.getData(ICC_Profile.icSigHead);
        int index = ICC_Profile.icHdrRenderingIntent;
        return (((header[index]   & 0xff) << 24) |
                ((header[index+1] & 0xff) << 16) |
                ((header[index+2] & 0xff) <<  8) |
                 (header[index+3] & 0xff));
    
private final java.awt.image.BufferedImagenonICCBIFilter(java.awt.image.BufferedImage src, java.awt.color.ColorSpace srcColorSpace, java.awt.image.BufferedImage dst, java.awt.color.ColorSpace dstColorSpace)


        int w = src.getWidth();
        int h = src.getHeight();
        ICC_ColorSpace ciespace =
            (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
        if (dst == null) {
            dst = createCompatibleDestImage(src, null);
            dstColorSpace = dst.getColorModel().getColorSpace();
        } else {
            if ((h != dst.getHeight()) || (w != dst.getWidth())) {
                throw new IllegalArgumentException(
                    "Width or height of BufferedImages do not match");
            }
        }
        Raster srcRas = src.getRaster();
        WritableRaster dstRas = dst.getRaster();
        ColorModel srcCM = src.getColorModel();
        ColorModel dstCM = dst.getColorModel();
        int srcNumComp = srcCM.getNumColorComponents();
        int dstNumComp = dstCM.getNumColorComponents();
        boolean dstHasAlpha = dstCM.hasAlpha();
        boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha;
        ColorSpace[] list;
        if ((CSList == null) && (profileList.length != 0)) {
            /* possible non-ICC src, some profiles, possible non-ICC dst */
            boolean nonICCSrc, nonICCDst;
            ICC_Profile srcProfile, dstProfile;
            if (!(srcColorSpace instanceof ICC_ColorSpace)) {
                nonICCSrc = true;
                srcProfile = ciespace.getProfile();
            } else {
                nonICCSrc = false;
                srcProfile = ((ICC_ColorSpace) srcColorSpace).getProfile();
            }
            if (!(dstColorSpace instanceof ICC_ColorSpace)) {
                nonICCDst = true;
                dstProfile = ciespace.getProfile();
            } else {
                nonICCDst = false;
                dstProfile = ((ICC_ColorSpace) dstColorSpace).getProfile();
            }
            /* make a new transform if needed */
            if ((thisTransform == null) || (thisSrcProfile != srcProfile) ||
                (thisDestProfile != dstProfile) ) {
                updateBITransform(srcProfile, dstProfile);
            }
            // process per scanline
            float maxNum = 65535.0f; // use 16-bit precision in CMM
            ColorSpace cs;
            int iccSrcNumComp;
            if (nonICCSrc) {
                cs = ciespace;
                iccSrcNumComp = 3;
            } else {
                cs = srcColorSpace;
                iccSrcNumComp = srcNumComp;
            }
            float[] srcMinVal = new float[iccSrcNumComp];
            float[] srcInvDiffMinMax = new float[iccSrcNumComp];
            for (int i = 0; i < srcNumComp; i++) {
                srcMinVal[i] = cs.getMinValue(i);
                srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]);
            }
            int iccDstNumComp;
            if (nonICCDst) {
                cs = ciespace;
                iccDstNumComp = 3;
            } else {
                cs = dstColorSpace;
                iccDstNumComp = dstNumComp;
            }
            float[] dstMinVal = new float[iccDstNumComp];
            float[] dstDiffMinMax = new float[iccDstNumComp];
            for (int i = 0; i < dstNumComp; i++) {
                dstMinVal[i] = cs.getMinValue(i);
                dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum;
            }
            float[] dstColor;
            if (dstHasAlpha) {
                int size = ((dstNumComp + 1) > 3) ? (dstNumComp + 1) : 3;
                dstColor = new float[size];
            } else {
                int size = (dstNumComp  > 3) ? dstNumComp : 3;
                dstColor = new float[size];
            }
            short[] srcLine = new short[w * iccSrcNumComp];
            short[] dstLine = new short[w * iccDstNumComp];
            Object pixel;
            float[] color;
            float[] alpha = null;
            if (needSrcAlpha) {
                alpha = new float[w];
            }
            int idx;
            // process each scanline
            for (int y = 0; y < h; y++) {
                // convert src scanline
                pixel = null;
                color = null;
                idx = 0;
                for (int x = 0; x < w; x++) {
                    pixel = srcRas.getDataElements(x, y, pixel);
                    color = srcCM.getNormalizedComponents(pixel, color, 0);
                    if (needSrcAlpha) {
                        alpha[x] = color[srcNumComp];
                    }
                    if (nonICCSrc) {
                        color = srcColorSpace.toCIEXYZ(color);
                    }
                    for (int i = 0; i < iccSrcNumComp; i++) {
                        srcLine[idx++] = (short)
                            ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
                             0.5f);
                    }
                }
                // color convert srcLine to dstLine
                thisTransform.colorConvert(srcLine, dstLine);
                // convert dst scanline
                pixel = null;
                idx = 0;
                for (int x = 0; x < w; x++) {
                    for (int i = 0; i < iccDstNumComp; i++) {
                        dstColor[i] = ((float) (dstLine[idx++] & 0xffff)) *
                                      dstDiffMinMax[i] + dstMinVal[i];
                    }
                    if (nonICCDst) {
                        color = srcColorSpace.fromCIEXYZ(dstColor);
                        for (int i = 0; i < dstNumComp; i++) {
                            dstColor[i] = color[i];
                        }
                    }
                    if (needSrcAlpha) {
                        dstColor[dstNumComp] = alpha[x];
                    } else if (dstHasAlpha) {
                        dstColor[dstNumComp] = 1.0f;
                    }
                    pixel = dstCM.getDataElements(dstColor, 0, pixel);
                    dstRas.setDataElements(x, y, pixel);
                }
            }
        } else {
            /* possible non-ICC src, possible CSList, possible non-ICC dst */
            // process per pixel
            int numCS;
            if (CSList == null) {
                numCS = 0;
            } else {
                numCS = CSList.length;
            }
            float[] dstColor;
            if (dstHasAlpha) {
                dstColor = new float[dstNumComp + 1];
            } else {
                dstColor = new float[dstNumComp];
            }
            Object spixel = null;
            Object dpixel = null;
            float[] color = null;
            float[] tmpColor;
            // process each pixel
            for (int y = 0; y < h; y++) {
                for (int x = 0; x < w; x++) {
                    spixel = srcRas.getDataElements(x, y, spixel);
                    color = srcCM.getNormalizedComponents(spixel, color, 0);
                    tmpColor = srcColorSpace.toCIEXYZ(color);
                    for (int i = 0; i < numCS; i++) {
                        tmpColor = CSList[i].fromCIEXYZ(tmpColor);
                        tmpColor = CSList[i].toCIEXYZ(tmpColor);
                    }
                    tmpColor = dstColorSpace.fromCIEXYZ(tmpColor);
                    for (int i = 0; i < dstNumComp; i++) {
                        dstColor[i] = tmpColor[i];
                    }
                    if (needSrcAlpha) {
                        dstColor[dstNumComp] = color[srcNumComp];
                    } else if (dstHasAlpha) {
                        dstColor[dstNumComp] = 1.0f;
                    }
                    dpixel = dstCM.getDataElements(dstColor, 0, dpixel);
                    dstRas.setDataElements(x, y, dpixel);
                    
                }
            }
        }

        return dst;
    
private final java.awt.image.WritableRasternonICCRasterFilter(java.awt.image.Raster src, java.awt.image.WritableRaster dst)


        if (CSList.length != 2) {
            throw new IllegalArgumentException(
                "Destination ColorSpace is undefined");
        }
        if (src.getNumBands() != CSList[0].getNumComponents()) {
            throw new IllegalArgumentException(
                "Numbers of source Raster bands and source color space " +
                "components do not match");
        }
        if (dst == null) {
            dst = createCompatibleDestRaster(src);
        } else {
            if (src.getHeight() != dst.getHeight() ||
                src.getWidth() != dst.getWidth()) {
                throw new IllegalArgumentException(
                    "Width or height of Rasters do not match");
            }
            if (dst.getNumBands() != CSList[1].getNumComponents()) {
                throw new IllegalArgumentException(
                    "Numbers of destination Raster bands and destination " +
                    "color space components do not match");
            }
        }

        if (srcMinVals == null) {
            getMinMaxValsFromColorSpaces(CSList[0], CSList[1]);
        }

        SampleModel srcSM = src.getSampleModel();
        SampleModel dstSM = dst.getSampleModel();
        boolean srcIsFloat, dstIsFloat;
        int srcTransferType = src.getTransferType();
        int dstTransferType = dst.getTransferType();
        if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
            (srcTransferType == DataBuffer.TYPE_DOUBLE)) {
            srcIsFloat = true;
        } else {
            srcIsFloat = false;
        }
        if ((dstTransferType == DataBuffer.TYPE_FLOAT) ||
            (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
            dstIsFloat = true;
        } else {
            dstIsFloat = false;
        }
        int w = src.getWidth();
        int h = src.getHeight();
        int srcNumBands = src.getNumBands();
        int dstNumBands = dst.getNumBands();
        float[] srcScaleFactor = null;
        float[] dstScaleFactor = null;
        if (!srcIsFloat) {
            srcScaleFactor = new float[srcNumBands];
            for (int i = 0; i < srcNumBands; i++) {
                if (srcTransferType == DataBuffer.TYPE_SHORT) {
                    srcScaleFactor[i] = (srcMaxVals[i] - srcMinVals[i]) /
                                        32767.0f;
                } else {
                    srcScaleFactor[i] = (srcMaxVals[i] - srcMinVals[i]) /
                        ((float) ((1 << srcSM.getSampleSize(i)) - 1));
                }
            }
        }
        if (!dstIsFloat) {
            dstScaleFactor = new float[dstNumBands];
            for (int i = 0; i < dstNumBands; i++) {
                if (dstTransferType == DataBuffer.TYPE_SHORT) {
                    dstScaleFactor[i] = 32767.0f /
                                        (dstMaxVals[i] - dstMinVals[i]);
                } else {
                    dstScaleFactor[i] = 
                        ((float) ((1 << dstSM.getSampleSize(i)) - 1)) /
                        (dstMaxVals[i] - dstMinVals[i]);
                }
            }
        }
        int ys = src.getMinY();
        int yd = dst.getMinY();
        int xs, xd;
        float sample;
        float[] color = new float[srcNumBands];
        float[] tmpColor;
        ColorSpace srcColorSpace = CSList[0];
        ColorSpace dstColorSpace = CSList[1];
        // process each pixel
        for (int y = 0; y < h; y++, ys++, yd++) {
            // get src scanline
            xs = src.getMinX();
            xd = dst.getMinX();
            for (int x = 0; x < w; x++, xs++, xd++) {
                for (int i = 0; i < srcNumBands; i++) {
                    sample = src.getSampleFloat(xs, ys, i);
                    if (!srcIsFloat) {
                        sample = sample * srcScaleFactor[i] + srcMinVals[i];
                    }
                    color[i] = sample;
                }
                tmpColor = srcColorSpace.toCIEXYZ(color);
                tmpColor = dstColorSpace.fromCIEXYZ(tmpColor);
                for (int i = 0; i < dstNumBands; i++) {
                    sample = tmpColor[i];
                    if (!dstIsFloat) {
                        sample = (sample - dstMinVals[i]) * dstScaleFactor[i];
                    }
                    dst.setSample(xd, yd, i, sample);
                }
            }
        }
        return dst;
    
private voidsetMinMax(int type, int nc, float[] minVals, float[] maxVals)

        if (type == ColorSpace.TYPE_Lab) {
            minVals[0] = 0.0f;    // L
            maxVals[0] = 100.0f;
            minVals[1] = -128.0f; // a
            maxVals[1] = 127.0f;
            minVals[2] = -128.0f; // b
            maxVals[2] = 127.0f;
        } else if (type == ColorSpace.TYPE_XYZ) {
            minVals[0] = minVals[1] = minVals[2] = 0.0f; // X, Y, Z
            maxVals[0] = maxVals[1] = maxVals[2] = 1.0f + (32767.0f/ 32768.0f);
        } else {
            for (int i = 0; i < nc; i++) {
                minVals[i] = 0.0f;
                maxVals[i] = 1.0f;
            }
        }
    
private voidupdateBITransform(java.awt.color.ICC_Profile srcProfile, java.awt.color.ICC_Profile destProfile)

        ICC_Profile[]    theProfiles;
        int              i1, nProfiles, nTransforms, whichTrans, renderState;
        ICC_Transform[]  theTransforms;
        boolean          useSrc = false, useDest = false;

        nProfiles = profileList.length;
        nTransforms = nProfiles;
        if ((nProfiles == 0) || (srcProfile != profileList[0])) {
            nTransforms += 1;
            useSrc = true;
        }
        if ((nProfiles == 0) || (destProfile != profileList[nProfiles - 1]) ||
            (nTransforms < 2)) {
            nTransforms += 1;
            useDest = true;
        }

        /* make the profile list */
        theProfiles = new ICC_Profile[nTransforms]; /* the list of profiles
                                                       for this Op */

        int idx = 0;
        if (useSrc) {
            /* insert source as first profile */
            theProfiles[idx++] = srcProfile;
        }

        for (i1 = 0; i1 < nProfiles; i1++) {
                                   /* insert profiles defined in this Op */
            theProfiles[idx++] = profileList [i1];
        }

        if (useDest) {
            /* insert dest as last profile */
            theProfiles[idx] = destProfile;
        }

        /* make the transform list */
        theTransforms = new ICC_Transform [nTransforms];

        /* initialize transform get loop */
        if (theProfiles[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) {
                                        /* if first profile is a printer
                                           render as colorimetric */
            renderState = ICC_Profile.icRelativeColorimetric;
        }
        else {
            renderState = ICC_Profile.icPerceptual; /* render any other
                                                       class perceptually */
        }

        whichTrans = ICC_Transform.In;

        /* get the transforms from each profile */
        for (i1 = 0; i1 < nTransforms; i1++) {
            if (i1 == nTransforms -1) {         /* last profile? */
                whichTrans = ICC_Transform.Out; /* get output transform */
            }
            else {	/* check for abstract profile */
                if ((whichTrans == ICC_Transform.Simulation) &&
                    (theProfiles[i1].getProfileClass () ==
                     ICC_Profile.CLASS_ABSTRACT)) {
            	renderState = ICC_Profile.icPerceptual;
                    whichTrans = ICC_Transform.In;
                }
            }

            theTransforms[i1] = new ICC_Transform (theProfiles[i1],
                                                   renderState, whichTrans);

            /* get this profile's rendering intent to select transform
               from next profile */
            renderState = getRenderingIntent(theProfiles[i1]);

            /* "middle" profiles use simulation transform */
            whichTrans = ICC_Transform.Simulation;
        }

        /* make the net transform */
        thisTransform = new ICC_Transform (theTransforms);

        /* update corresponding source and dest profiles */
        thisSrcProfile = srcProfile;
        thisDestProfile = destProfile;