jdk/src/share/classes/java/awt/image/ColorConvertOp.java
changeset 539 7952521a4ad3
child 715 f16baef3a20e
equal deleted inserted replaced
538:d95bc71a5732 539:7952521a4ad3
       
     1 /*
       
     2  * Portions Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 /**********************************************************************
       
    27  **********************************************************************
       
    28  **********************************************************************
       
    29  *** COPYRIGHT (c) Eastman Kodak Company, 1997                      ***
       
    30  *** As  an unpublished  work pursuant to Title 17 of the United    ***
       
    31  *** States Code.  All rights reserved.                             ***
       
    32  **********************************************************************
       
    33  **********************************************************************
       
    34  **********************************************************************/
       
    35 
       
    36 package java.awt.image;
       
    37 
       
    38 import java.awt.Point;
       
    39 import java.awt.Graphics2D;
       
    40 import java.awt.color.*;
       
    41 import sun.java2d.cmm.ColorTransform;
       
    42 import sun.java2d.cmm.CMSManager;
       
    43 import sun.java2d.cmm.ProfileDeferralMgr;
       
    44 import sun.java2d.cmm.PCMM;
       
    45 import java.awt.geom.Rectangle2D;
       
    46 import java.awt.geom.Point2D;
       
    47 import java.awt.RenderingHints;
       
    48 
       
    49 /**
       
    50  * This class performs a pixel-by-pixel color conversion of the data in
       
    51  * the source image.  The resulting color values are scaled to the precision
       
    52  * of the destination image.  Color conversion can be specified
       
    53  * via an array of ColorSpace objects or an array of ICC_Profile objects.
       
    54  * <p>
       
    55  * If the source is a BufferedImage with premultiplied alpha, the
       
    56  * color components are divided by the alpha component before color conversion.
       
    57  * If the destination is a BufferedImage with premultiplied alpha, the
       
    58  * color components are multiplied by the alpha component after conversion.
       
    59  * Rasters are treated as having no alpha channel, i.e. all bands are
       
    60  * color bands.
       
    61  * <p>
       
    62  * If a RenderingHints object is specified in the constructor, the
       
    63  * color rendering hint and the dithering hint may be used to control
       
    64  * color conversion.
       
    65  * <p>
       
    66  * Note that Source and Destination may be the same object.
       
    67  * <p>
       
    68  * @see java.awt.RenderingHints#KEY_COLOR_RENDERING
       
    69  * @see java.awt.RenderingHints#KEY_DITHERING
       
    70  */
       
    71 public class ColorConvertOp implements BufferedImageOp, RasterOp {
       
    72     ICC_Profile[]    profileList;
       
    73     ColorSpace[]     CSList;
       
    74     ColorTransform    thisTransform, thisRasterTransform;
       
    75     ICC_Profile      thisSrcProfile, thisDestProfile;
       
    76     RenderingHints   hints;
       
    77     boolean          gotProfiles;
       
    78     float[]          srcMinVals, srcMaxVals, dstMinVals, dstMaxVals;
       
    79 
       
    80     /* the class initializer */
       
    81     static {
       
    82         if (ProfileDeferralMgr.deferring) {
       
    83             ProfileDeferralMgr.activateProfiles();
       
    84         }
       
    85     }
       
    86 
       
    87     /**
       
    88      * Constructs a new ColorConvertOp which will convert
       
    89      * from a source color space to a destination color space.
       
    90      * The RenderingHints argument may be null.
       
    91      * This Op can be used only with BufferedImages, and will convert
       
    92      * directly from the ColorSpace of the source image to that of the
       
    93      * destination.  The destination argument of the filter method
       
    94      * cannot be specified as null.
       
    95      * @param hints the <code>RenderingHints</code> object used to control
       
    96      *        the color conversion, or <code>null</code>
       
    97      */
       
    98     public ColorConvertOp (RenderingHints hints)
       
    99     {
       
   100         profileList = new ICC_Profile [0];    /* 0 length list */
       
   101         this.hints  = hints;
       
   102     }
       
   103 
       
   104     /**
       
   105      * Constructs a new ColorConvertOp from a ColorSpace object.
       
   106      * The RenderingHints argument may be null.  This
       
   107      * Op can be used only with BufferedImages, and is primarily useful
       
   108      * when the {@link #filter(BufferedImage, BufferedImage) filter}
       
   109      * method is invoked with a destination argument of null.
       
   110      * In that case, the ColorSpace defines the destination color space
       
   111      * for the destination created by the filter method.  Otherwise, the
       
   112      * ColorSpace defines an intermediate space to which the source is
       
   113      * converted before being converted to the destination space.
       
   114      * @param cspace defines the destination <code>ColorSpace</code> or an
       
   115      *        intermediate <code>ColorSpace</code>
       
   116      * @param hints the <code>RenderingHints</code> object used to control
       
   117      *        the color conversion, or <code>null</code>
       
   118      * @throws NullPointerException if cspace is null
       
   119      */
       
   120     public ColorConvertOp (ColorSpace cspace, RenderingHints hints)
       
   121     {
       
   122         if (cspace == null) {
       
   123             throw new NullPointerException("ColorSpace cannot be null");
       
   124         }
       
   125         if (cspace instanceof ICC_ColorSpace) {
       
   126             profileList = new ICC_Profile [1];    /* 1 profile in the list */
       
   127 
       
   128             profileList [0] = ((ICC_ColorSpace) cspace).getProfile();
       
   129         }
       
   130         else {
       
   131             CSList = new ColorSpace[1]; /* non-ICC case: 1 ColorSpace in list */
       
   132             CSList[0] = cspace;
       
   133         }
       
   134         this.hints  = hints;
       
   135     }
       
   136 
       
   137 
       
   138     /**
       
   139      * Constructs a new ColorConvertOp from two ColorSpace objects.
       
   140      * The RenderingHints argument may be null.
       
   141      * This Op is primarily useful for calling the filter method on
       
   142      * Rasters, in which case the two ColorSpaces define the operation
       
   143      * to be performed on the Rasters.  In that case, the number of bands
       
   144      * in the source Raster must match the number of components in
       
   145      * srcCspace, and the number of bands in the destination Raster
       
   146      * must match the number of components in dstCspace.  For BufferedImages,
       
   147      * the two ColorSpaces define intermediate spaces through which the
       
   148      * source is converted before being converted to the destination space.
       
   149      * @param srcCspace the source <code>ColorSpace</code>
       
   150      * @param dstCspace the destination <code>ColorSpace</code>
       
   151      * @param hints the <code>RenderingHints</code> object used to control
       
   152      *        the color conversion, or <code>null</code>
       
   153      * @throws NullPointerException if either srcCspace or dstCspace is null
       
   154      */
       
   155     public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace,
       
   156                            RenderingHints hints)
       
   157     {
       
   158         if ((srcCspace == null) || (dstCspace == null)) {
       
   159             throw new NullPointerException("ColorSpaces cannot be null");
       
   160         }
       
   161         if ((srcCspace instanceof ICC_ColorSpace) &&
       
   162             (dstCspace instanceof ICC_ColorSpace)) {
       
   163             profileList = new ICC_Profile [2];    /* 2 profiles in the list */
       
   164 
       
   165             profileList [0] = ((ICC_ColorSpace) srcCspace).getProfile();
       
   166             profileList [1] = ((ICC_ColorSpace) dstCspace).getProfile();
       
   167 
       
   168             getMinMaxValsFromColorSpaces(srcCspace, dstCspace);
       
   169         } else {
       
   170             /* non-ICC case: 2 ColorSpaces in list */
       
   171             CSList = new ColorSpace[2];
       
   172             CSList[0] = srcCspace;
       
   173             CSList[1] = dstCspace;
       
   174         }
       
   175         this.hints  = hints;
       
   176     }
       
   177 
       
   178 
       
   179      /**
       
   180      * Constructs a new ColorConvertOp from an array of ICC_Profiles.
       
   181      * The RenderingHints argument may be null.
       
   182      * The sequence of profiles may include profiles that represent color
       
   183      * spaces, profiles that represent effects, etc.  If the whole sequence
       
   184      * does not represent a well-defined color conversion, an exception is
       
   185      * thrown.
       
   186      * <p>For BufferedImages, if the ColorSpace
       
   187      * of the source BufferedImage does not match the requirements of the
       
   188      * first profile in the array,
       
   189      * the first conversion is to an appropriate ColorSpace.
       
   190      * If the requirements of the last profile in the array are not met
       
   191      * by the ColorSpace of the destination BufferedImage,
       
   192      * the last conversion is to the destination's ColorSpace.
       
   193      * <p>For Rasters, the number of bands in the source Raster must match
       
   194      * the requirements of the first profile in the array, and the
       
   195      * number of bands in the destination Raster must match the requirements
       
   196      * of the last profile in the array.  The array must have at least two
       
   197      * elements or calling the filter method for Rasters will throw an
       
   198      * IllegalArgumentException.
       
   199      * @param profiles the array of <code>ICC_Profile</code> objects
       
   200      * @param hints the <code>RenderingHints</code> object used to control
       
   201      *        the color conversion, or <code>null</code>
       
   202      * @exception IllegalArgumentException when the profile sequence does not
       
   203      *             specify a well-defined color conversion
       
   204      * @exception NullPointerException if profiles is null
       
   205      */
       
   206     public ColorConvertOp (ICC_Profile[] profiles, RenderingHints hints)
       
   207     {
       
   208         if (profiles == null) {
       
   209             throw new NullPointerException("Profiles cannot be null");
       
   210         }
       
   211         gotProfiles = true;
       
   212         profileList = new ICC_Profile[profiles.length];
       
   213         for (int i1 = 0; i1 < profiles.length; i1++) {
       
   214             profileList[i1] = profiles[i1];
       
   215         }
       
   216         this.hints  = hints;
       
   217     }
       
   218 
       
   219 
       
   220     /**
       
   221      * Returns the array of ICC_Profiles used to construct this ColorConvertOp.
       
   222      * Returns null if the ColorConvertOp was not constructed from such an
       
   223      * array.
       
   224      * @return the array of <code>ICC_Profile</code> objects of this
       
   225      *         <code>ColorConvertOp</code>, or <code>null</code> if this
       
   226      *         <code>ColorConvertOp</code> was not constructed with an
       
   227      *         array of <code>ICC_Profile</code> objects.
       
   228      */
       
   229     public final ICC_Profile[] getICC_Profiles() {
       
   230         if (gotProfiles) {
       
   231             ICC_Profile[] profiles = new ICC_Profile[profileList.length];
       
   232             for (int i1 = 0; i1 < profileList.length; i1++) {
       
   233                 profiles[i1] = profileList[i1];
       
   234             }
       
   235             return profiles;
       
   236         }
       
   237         return null;
       
   238     }
       
   239 
       
   240     /**
       
   241      * ColorConverts the source BufferedImage.
       
   242      * If the destination image is null,
       
   243      * a BufferedImage will be created with an appropriate ColorModel.
       
   244      * @param src the source <code>BufferedImage</code> to be converted
       
   245      * @param dest the destination <code>BufferedImage</code>,
       
   246      *        or <code>null</code>
       
   247      * @return <code>dest</code> color converted from <code>src</code>
       
   248      *         or a new, converted <code>BufferedImage</code>
       
   249      *         if <code>dest</code> is <code>null</code>
       
   250      * @exception IllegalArgumentException if dest is null and this op was
       
   251      *             constructed using the constructor which takes only a
       
   252      *             RenderingHints argument, since the operation is ill defined.
       
   253      */
       
   254     public final BufferedImage filter(BufferedImage src, BufferedImage dest) {
       
   255         ColorSpace srcColorSpace, destColorSpace;
       
   256         BufferedImage savdest = null;
       
   257 
       
   258         if (src.getColorModel() instanceof IndexColorModel) {
       
   259             IndexColorModel icm = (IndexColorModel) src.getColorModel();
       
   260             src = icm.convertToIntDiscrete(src.getRaster(), true);
       
   261         }
       
   262         srcColorSpace = src.getColorModel().getColorSpace();
       
   263         if (dest != null) {
       
   264             if (dest.getColorModel() instanceof IndexColorModel) {
       
   265                 savdest = dest;
       
   266                 dest = null;
       
   267                 destColorSpace = null;
       
   268             } else {
       
   269                 destColorSpace = dest.getColorModel().getColorSpace();
       
   270             }
       
   271         } else {
       
   272             destColorSpace = null;
       
   273         }
       
   274 
       
   275         if ((CSList != null) ||
       
   276             (!(srcColorSpace instanceof ICC_ColorSpace)) ||
       
   277             ((dest != null) &&
       
   278              (!(destColorSpace instanceof ICC_ColorSpace)))) {
       
   279             /* non-ICC case */
       
   280             dest = nonICCBIFilter(src, srcColorSpace, dest, destColorSpace);
       
   281         } else {
       
   282             dest = ICCBIFilter(src, srcColorSpace, dest, destColorSpace);
       
   283         }
       
   284 
       
   285         if (savdest != null) {
       
   286             Graphics2D big = savdest.createGraphics();
       
   287             try {
       
   288                 big.drawImage(dest, 0, 0, null);
       
   289             } finally {
       
   290                 big.dispose();
       
   291             }
       
   292             return savdest;
       
   293         } else {
       
   294             return dest;
       
   295         }
       
   296     }
       
   297 
       
   298     private final BufferedImage ICCBIFilter(BufferedImage src,
       
   299                                             ColorSpace srcColorSpace,
       
   300                                             BufferedImage dest,
       
   301                                             ColorSpace destColorSpace) {
       
   302     int              nProfiles = profileList.length;
       
   303     ICC_Profile      srcProfile = null, destProfile = null;
       
   304 
       
   305         srcProfile = ((ICC_ColorSpace) srcColorSpace).getProfile();
       
   306 
       
   307         if (dest == null) {        /* last profile in the list defines
       
   308                                       the output color space */
       
   309             if (nProfiles == 0) {
       
   310                 throw new IllegalArgumentException(
       
   311                     "Destination ColorSpace is undefined");
       
   312             }
       
   313             destProfile = profileList [nProfiles - 1];
       
   314             dest = createCompatibleDestImage(src, null);
       
   315         }
       
   316         else {
       
   317             if (src.getHeight() != dest.getHeight() ||
       
   318                 src.getWidth() != dest.getWidth()) {
       
   319                 throw new IllegalArgumentException(
       
   320                     "Width or height of BufferedImages do not match");
       
   321             }
       
   322             destProfile = ((ICC_ColorSpace) destColorSpace).getProfile();
       
   323         }
       
   324 
       
   325         /* Checking if all profiles in the transform sequence are the same.
       
   326          * If so, performing just copying the data.
       
   327          */
       
   328         if (srcProfile == destProfile) {
       
   329             boolean noTrans = true;
       
   330             for (int i = 0; i < nProfiles; i++) {
       
   331                 if (srcProfile != profileList[i]) {
       
   332                     noTrans = false;
       
   333                     break;
       
   334                 }
       
   335             }
       
   336             if (noTrans) {
       
   337                 Graphics2D g = dest.createGraphics();
       
   338                 try {
       
   339                     g.drawImage(src, 0, 0, null);
       
   340                 } finally {
       
   341                     g.dispose();
       
   342                 }
       
   343 
       
   344                 return dest;
       
   345             }
       
   346         }
       
   347 
       
   348         /* make a new transform if needed */
       
   349         if ((thisTransform == null) || (thisSrcProfile != srcProfile) ||
       
   350             (thisDestProfile != destProfile) ) {
       
   351             updateBITransform(srcProfile, destProfile);
       
   352         }
       
   353 
       
   354         /* color convert the image */
       
   355         thisTransform.colorConvert(src, dest);
       
   356 
       
   357         return dest;
       
   358     }
       
   359 
       
   360     private void updateBITransform(ICC_Profile srcProfile,
       
   361                                    ICC_Profile destProfile) {
       
   362         ICC_Profile[]    theProfiles;
       
   363         int              i1, nProfiles, nTransforms, whichTrans, renderState;
       
   364         ColorTransform[]  theTransforms;
       
   365         boolean          useSrc = false, useDest = false;
       
   366 
       
   367         nProfiles = profileList.length;
       
   368         nTransforms = nProfiles;
       
   369         if ((nProfiles == 0) || (srcProfile != profileList[0])) {
       
   370             nTransforms += 1;
       
   371             useSrc = true;
       
   372         }
       
   373         if ((nProfiles == 0) || (destProfile != profileList[nProfiles - 1]) ||
       
   374             (nTransforms < 2)) {
       
   375             nTransforms += 1;
       
   376             useDest = true;
       
   377         }
       
   378 
       
   379         /* make the profile list */
       
   380         theProfiles = new ICC_Profile[nTransforms]; /* the list of profiles
       
   381                                                        for this Op */
       
   382 
       
   383         int idx = 0;
       
   384         if (useSrc) {
       
   385             /* insert source as first profile */
       
   386             theProfiles[idx++] = srcProfile;
       
   387         }
       
   388 
       
   389         for (i1 = 0; i1 < nProfiles; i1++) {
       
   390                                    /* insert profiles defined in this Op */
       
   391             theProfiles[idx++] = profileList [i1];
       
   392         }
       
   393 
       
   394         if (useDest) {
       
   395             /* insert dest as last profile */
       
   396             theProfiles[idx] = destProfile;
       
   397         }
       
   398 
       
   399         /* make the transform list */
       
   400         theTransforms = new ColorTransform [nTransforms];
       
   401 
       
   402         /* initialize transform get loop */
       
   403         if (theProfiles[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) {
       
   404                                         /* if first profile is a printer
       
   405                                            render as colorimetric */
       
   406             renderState = ICC_Profile.icRelativeColorimetric;
       
   407         }
       
   408         else {
       
   409             renderState = ICC_Profile.icPerceptual; /* render any other
       
   410                                                        class perceptually */
       
   411         }
       
   412 
       
   413         whichTrans = ColorTransform.In;
       
   414 
       
   415         PCMM mdl = CMSManager.getModule();
       
   416 
       
   417         /* get the transforms from each profile */
       
   418         for (i1 = 0; i1 < nTransforms; i1++) {
       
   419             if (i1 == nTransforms -1) {         /* last profile? */
       
   420                 whichTrans = ColorTransform.Out; /* get output transform */
       
   421             }
       
   422             else {      /* check for abstract profile */
       
   423                 if ((whichTrans == ColorTransform.Simulation) &&
       
   424                     (theProfiles[i1].getProfileClass () ==
       
   425                      ICC_Profile.CLASS_ABSTRACT)) {
       
   426                 renderState = ICC_Profile.icPerceptual;
       
   427                     whichTrans = ColorTransform.In;
       
   428                 }
       
   429             }
       
   430 
       
   431             theTransforms[i1] = mdl.createTransform (
       
   432                 theProfiles[i1], renderState, whichTrans);
       
   433 
       
   434             /* get this profile's rendering intent to select transform
       
   435                from next profile */
       
   436             renderState = getRenderingIntent(theProfiles[i1]);
       
   437 
       
   438             /* "middle" profiles use simulation transform */
       
   439             whichTrans = ColorTransform.Simulation;
       
   440         }
       
   441 
       
   442         /* make the net transform */
       
   443         thisTransform = mdl.createTransform(theTransforms);
       
   444 
       
   445         /* update corresponding source and dest profiles */
       
   446         thisSrcProfile = srcProfile;
       
   447         thisDestProfile = destProfile;
       
   448     }
       
   449 
       
   450     /**
       
   451      * ColorConverts the image data in the source Raster.
       
   452      * If the destination Raster is null, a new Raster will be created.
       
   453      * The number of bands in the source and destination Rasters must
       
   454      * meet the requirements explained above.  The constructor used to
       
   455      * create this ColorConvertOp must have provided enough information
       
   456      * to define both source and destination color spaces.  See above.
       
   457      * Otherwise, an exception is thrown.
       
   458      * @param src the source <code>Raster</code> to be converted
       
   459      * @param dest the destination <code>WritableRaster</code>,
       
   460      *        or <code>null</code>
       
   461      * @return <code>dest</code> color converted from <code>src</code>
       
   462      *         or a new, converted <code>WritableRaster</code>
       
   463      *         if <code>dest</code> is <code>null</code>
       
   464      * @exception IllegalArgumentException if the number of source or
       
   465      *             destination bands is incorrect, the source or destination
       
   466      *             color spaces are undefined, or this op was constructed
       
   467      *             with one of the constructors that applies only to
       
   468      *             operations on BufferedImages.
       
   469      */
       
   470     public final WritableRaster filter (Raster src, WritableRaster dest)  {
       
   471 
       
   472         if (CSList != null) {
       
   473             /* non-ICC case */
       
   474             return nonICCRasterFilter(src, dest);
       
   475         }
       
   476         int nProfiles = profileList.length;
       
   477         if (nProfiles < 2) {
       
   478             throw new IllegalArgumentException(
       
   479                 "Source or Destination ColorSpace is undefined");
       
   480         }
       
   481         if (src.getNumBands() != profileList[0].getNumComponents()) {
       
   482             throw new IllegalArgumentException(
       
   483                 "Numbers of source Raster bands and source color space " +
       
   484                 "components do not match");
       
   485         }
       
   486         if (dest == null) {
       
   487             dest = createCompatibleDestRaster(src);
       
   488         }
       
   489         else {
       
   490             if (src.getHeight() != dest.getHeight() ||
       
   491                 src.getWidth() != dest.getWidth()) {
       
   492                 throw new IllegalArgumentException(
       
   493                     "Width or height of Rasters do not match");
       
   494             }
       
   495             if (dest.getNumBands() !=
       
   496                 profileList[nProfiles-1].getNumComponents()) {
       
   497                 throw new IllegalArgumentException(
       
   498                     "Numbers of destination Raster bands and destination " +
       
   499                     "color space components do not match");
       
   500             }
       
   501         }
       
   502 
       
   503         /* make a new transform if needed */
       
   504         if (thisRasterTransform == null) {
       
   505             int              i1, whichTrans, renderState;
       
   506             ColorTransform[]  theTransforms;
       
   507 
       
   508             /* make the transform list */
       
   509             theTransforms = new ColorTransform [nProfiles];
       
   510 
       
   511             /* initialize transform get loop */
       
   512             if (profileList[0].getProfileClass() == ICC_Profile.CLASS_OUTPUT) {
       
   513                                             /* if first profile is a printer
       
   514                                                render as colorimetric */
       
   515                 renderState = ICC_Profile.icRelativeColorimetric;
       
   516             }
       
   517             else {
       
   518                 renderState = ICC_Profile.icPerceptual; /* render any other
       
   519                                                            class perceptually */
       
   520             }
       
   521 
       
   522             whichTrans = ColorTransform.In;
       
   523 
       
   524             PCMM mdl = CMSManager.getModule();
       
   525 
       
   526             /* get the transforms from each profile */
       
   527             for (i1 = 0; i1 < nProfiles; i1++) {
       
   528                 if (i1 == nProfiles -1) {         /* last profile? */
       
   529                     whichTrans = ColorTransform.Out; /* get output transform */
       
   530                 }
       
   531                 else {  /* check for abstract profile */
       
   532                     if ((whichTrans == ColorTransform.Simulation) &&
       
   533                         (profileList[i1].getProfileClass () ==
       
   534                          ICC_Profile.CLASS_ABSTRACT)) {
       
   535                         renderState = ICC_Profile.icPerceptual;
       
   536                         whichTrans = ColorTransform.In;
       
   537                     }
       
   538                 }
       
   539 
       
   540                 theTransforms[i1] = mdl.createTransform (
       
   541                     profileList[i1], renderState, whichTrans);
       
   542 
       
   543                 /* get this profile's rendering intent to select transform
       
   544                    from next profile */
       
   545                 renderState = getRenderingIntent(profileList[i1]);
       
   546 
       
   547                 /* "middle" profiles use simulation transform */
       
   548                 whichTrans = ColorTransform.Simulation;
       
   549             }
       
   550 
       
   551             /* make the net transform */
       
   552             thisRasterTransform = mdl.createTransform(theTransforms);
       
   553         }
       
   554 
       
   555         int srcTransferType = src.getTransferType();
       
   556         int dstTransferType = dest.getTransferType();
       
   557         if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
       
   558             (srcTransferType == DataBuffer.TYPE_DOUBLE) ||
       
   559             (dstTransferType == DataBuffer.TYPE_FLOAT) ||
       
   560             (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
       
   561             if (srcMinVals == null) {
       
   562                 getMinMaxValsFromProfiles(profileList[0],
       
   563                                           profileList[nProfiles-1]);
       
   564             }
       
   565             /* color convert the raster */
       
   566             thisRasterTransform.colorConvert(src, dest,
       
   567                                              srcMinVals, srcMaxVals,
       
   568                                              dstMinVals, dstMaxVals);
       
   569         } else {
       
   570             /* color convert the raster */
       
   571             thisRasterTransform.colorConvert(src, dest);
       
   572         }
       
   573 
       
   574 
       
   575         return dest;
       
   576     }
       
   577 
       
   578     /**
       
   579      * Returns the bounding box of the destination, given this source.
       
   580      * Note that this will be the same as the the bounding box of the
       
   581      * source.
       
   582      * @param src the source <code>BufferedImage</code>
       
   583      * @return a <code>Rectangle2D</code> that is the bounding box
       
   584      *         of the destination, given the specified <code>src</code>
       
   585      */
       
   586     public final Rectangle2D getBounds2D (BufferedImage src) {
       
   587         return getBounds2D(src.getRaster());
       
   588     }
       
   589 
       
   590     /**
       
   591      * Returns the bounding box of the destination, given this source.
       
   592      * Note that this will be the same as the the bounding box of the
       
   593      * source.
       
   594      * @param src the source <code>Raster</code>
       
   595      * @return a <code>Rectangle2D</code> that is the bounding box
       
   596      *         of the destination, given the specified <code>src</code>
       
   597      */
       
   598     public final Rectangle2D getBounds2D (Raster src) {
       
   599         /*        return new Rectangle (src.getXOffset(),
       
   600                               src.getYOffset(),
       
   601                               src.getWidth(), src.getHeight()); */
       
   602         return src.getBounds();
       
   603     }
       
   604 
       
   605     /**
       
   606      * Creates a zeroed destination image with the correct size and number of
       
   607      * bands, given this source.
       
   608      * @param src       Source image for the filter operation.
       
   609      * @param destCM    ColorModel of the destination.  If null, an
       
   610      *                  appropriate ColorModel will be used.
       
   611      * @return a <code>BufferedImage</code> with the correct size and
       
   612      * number of bands from the specified <code>src</code>.
       
   613      * @throws IllegalArgumentException if <code>destCM</code> is
       
   614      *         <code>null</code> and this <code>ColorConvertOp</code> was
       
   615      *         created without any <code>ICC_Profile</code> or
       
   616      *         <code>ColorSpace</code> defined for the destination
       
   617      */
       
   618     public BufferedImage createCompatibleDestImage (BufferedImage src,
       
   619                                                     ColorModel destCM) {
       
   620         ColorSpace cs = null;;
       
   621         if (destCM == null) {
       
   622             if (CSList == null) {
       
   623                 /* ICC case */
       
   624                 int nProfiles = profileList.length;
       
   625                 if (nProfiles == 0) {
       
   626                     throw new IllegalArgumentException(
       
   627                         "Destination ColorSpace is undefined");
       
   628                 }
       
   629                 ICC_Profile destProfile = profileList[nProfiles - 1];
       
   630                 cs = new ICC_ColorSpace(destProfile);
       
   631             } else {
       
   632                 /* non-ICC case */
       
   633                 int nSpaces = CSList.length;
       
   634                 cs = CSList[nSpaces - 1];
       
   635             }
       
   636         }
       
   637         return createCompatibleDestImage(src, destCM, cs);
       
   638     }
       
   639 
       
   640     private BufferedImage createCompatibleDestImage(BufferedImage src,
       
   641                                                     ColorModel destCM,
       
   642                                                     ColorSpace destCS) {
       
   643         BufferedImage image;
       
   644         if (destCM == null) {
       
   645             ColorModel srcCM = src.getColorModel();
       
   646             int nbands = destCS.getNumComponents();
       
   647             boolean hasAlpha = srcCM.hasAlpha();
       
   648             if (hasAlpha) {
       
   649                nbands += 1;
       
   650             }
       
   651             int[] nbits = new int[nbands];
       
   652             for (int i = 0; i < nbands; i++) {
       
   653                 nbits[i] = 8;
       
   654             }
       
   655             destCM = new ComponentColorModel(destCS, nbits, hasAlpha,
       
   656                                              srcCM.isAlphaPremultiplied(),
       
   657                                              srcCM.getTransparency(),
       
   658                                              DataBuffer.TYPE_BYTE);
       
   659         }
       
   660         int w = src.getWidth();
       
   661         int h = src.getHeight();
       
   662         image = new BufferedImage(destCM,
       
   663                                   destCM.createCompatibleWritableRaster(w, h),
       
   664                                   destCM.isAlphaPremultiplied(), null);
       
   665         return image;
       
   666     }
       
   667 
       
   668 
       
   669     /**
       
   670      * Creates a zeroed destination Raster with the correct size and number of
       
   671      * bands, given this source.
       
   672      * @param src the specified <code>Raster</code>
       
   673      * @return a <code>WritableRaster</code> with the correct size and number
       
   674      *         of bands from the specified <code>src</code>
       
   675      * @throws IllegalArgumentException if this <code>ColorConvertOp</code>
       
   676      *         was created without sufficient information to define the
       
   677      *         <code>dst</code> and <code>src</code> color spaces
       
   678      */
       
   679     public WritableRaster createCompatibleDestRaster (Raster src) {
       
   680         int ncomponents;
       
   681 
       
   682         if (CSList != null) {
       
   683             /* non-ICC case */
       
   684             if (CSList.length != 2) {
       
   685                 throw new IllegalArgumentException(
       
   686                     "Destination ColorSpace is undefined");
       
   687             }
       
   688             ncomponents = CSList[1].getNumComponents();
       
   689         } else {
       
   690             /* ICC case */
       
   691             int nProfiles = profileList.length;
       
   692             if (nProfiles < 2) {
       
   693                 throw new IllegalArgumentException(
       
   694                     "Destination ColorSpace is undefined");
       
   695             }
       
   696             ncomponents = profileList[nProfiles-1].getNumComponents();
       
   697         }
       
   698 
       
   699         WritableRaster dest =
       
   700             Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
       
   701                                   src.getWidth(),
       
   702                                   src.getHeight(),
       
   703                                   ncomponents,
       
   704                                   new Point(src.getMinX(), src.getMinY()));
       
   705         return dest;
       
   706     }
       
   707 
       
   708     /**
       
   709      * Returns the location of the destination point given a
       
   710      * point in the source.  If <code>dstPt</code> is non-null,
       
   711      * it will be used to hold the return value.  Note that
       
   712      * for this class, the destination point will be the same
       
   713      * as the source point.
       
   714      * @param srcPt the specified source <code>Point2D</code>
       
   715      * @param dstPt the destination <code>Point2D</code>
       
   716      * @return <code>dstPt</code> after setting its location to be
       
   717      *         the same as <code>srcPt</code>
       
   718      */
       
   719     public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt) {
       
   720         if (dstPt == null) {
       
   721             dstPt = new Point2D.Float();
       
   722         }
       
   723         dstPt.setLocation(srcPt.getX(), srcPt.getY());
       
   724 
       
   725         return dstPt;
       
   726     }
       
   727 
       
   728 
       
   729     /**
       
   730      * Returns the RenderingIntent from the specified ICC Profile.
       
   731      */
       
   732     private int getRenderingIntent (ICC_Profile profile) {
       
   733         byte[] header = profile.getData(ICC_Profile.icSigHead);
       
   734         int index = ICC_Profile.icHdrRenderingIntent;
       
   735         return (((header[index]   & 0xff) << 24) |
       
   736                 ((header[index+1] & 0xff) << 16) |
       
   737                 ((header[index+2] & 0xff) <<  8) |
       
   738                  (header[index+3] & 0xff));
       
   739     }
       
   740 
       
   741     /**
       
   742      * Returns the rendering hints used by this op.
       
   743      * @return the <code>RenderingHints</code> object of this
       
   744      *         <code>ColorConvertOp</code>
       
   745      */
       
   746     public final RenderingHints getRenderingHints() {
       
   747         return hints;
       
   748     }
       
   749 
       
   750     private final BufferedImage nonICCBIFilter(BufferedImage src,
       
   751                                                ColorSpace srcColorSpace,
       
   752                                                BufferedImage dst,
       
   753                                                ColorSpace dstColorSpace) {
       
   754 
       
   755         int w = src.getWidth();
       
   756         int h = src.getHeight();
       
   757         ICC_ColorSpace ciespace =
       
   758             (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
       
   759         if (dst == null) {
       
   760             dst = createCompatibleDestImage(src, null);
       
   761             dstColorSpace = dst.getColorModel().getColorSpace();
       
   762         } else {
       
   763             if ((h != dst.getHeight()) || (w != dst.getWidth())) {
       
   764                 throw new IllegalArgumentException(
       
   765                     "Width or height of BufferedImages do not match");
       
   766             }
       
   767         }
       
   768         Raster srcRas = src.getRaster();
       
   769         WritableRaster dstRas = dst.getRaster();
       
   770         ColorModel srcCM = src.getColorModel();
       
   771         ColorModel dstCM = dst.getColorModel();
       
   772         int srcNumComp = srcCM.getNumColorComponents();
       
   773         int dstNumComp = dstCM.getNumColorComponents();
       
   774         boolean dstHasAlpha = dstCM.hasAlpha();
       
   775         boolean needSrcAlpha = srcCM.hasAlpha() && dstHasAlpha;
       
   776         ColorSpace[] list;
       
   777         if ((CSList == null) && (profileList.length != 0)) {
       
   778             /* possible non-ICC src, some profiles, possible non-ICC dst */
       
   779             boolean nonICCSrc, nonICCDst;
       
   780             ICC_Profile srcProfile, dstProfile;
       
   781             if (!(srcColorSpace instanceof ICC_ColorSpace)) {
       
   782                 nonICCSrc = true;
       
   783                 srcProfile = ciespace.getProfile();
       
   784             } else {
       
   785                 nonICCSrc = false;
       
   786                 srcProfile = ((ICC_ColorSpace) srcColorSpace).getProfile();
       
   787             }
       
   788             if (!(dstColorSpace instanceof ICC_ColorSpace)) {
       
   789                 nonICCDst = true;
       
   790                 dstProfile = ciespace.getProfile();
       
   791             } else {
       
   792                 nonICCDst = false;
       
   793                 dstProfile = ((ICC_ColorSpace) dstColorSpace).getProfile();
       
   794             }
       
   795             /* make a new transform if needed */
       
   796             if ((thisTransform == null) || (thisSrcProfile != srcProfile) ||
       
   797                 (thisDestProfile != dstProfile) ) {
       
   798                 updateBITransform(srcProfile, dstProfile);
       
   799             }
       
   800             // process per scanline
       
   801             float maxNum = 65535.0f; // use 16-bit precision in CMM
       
   802             ColorSpace cs;
       
   803             int iccSrcNumComp;
       
   804             if (nonICCSrc) {
       
   805                 cs = ciespace;
       
   806                 iccSrcNumComp = 3;
       
   807             } else {
       
   808                 cs = srcColorSpace;
       
   809                 iccSrcNumComp = srcNumComp;
       
   810             }
       
   811             float[] srcMinVal = new float[iccSrcNumComp];
       
   812             float[] srcInvDiffMinMax = new float[iccSrcNumComp];
       
   813             for (int i = 0; i < srcNumComp; i++) {
       
   814                 srcMinVal[i] = cs.getMinValue(i);
       
   815                 srcInvDiffMinMax[i] = maxNum / (cs.getMaxValue(i) - srcMinVal[i]);
       
   816             }
       
   817             int iccDstNumComp;
       
   818             if (nonICCDst) {
       
   819                 cs = ciespace;
       
   820                 iccDstNumComp = 3;
       
   821             } else {
       
   822                 cs = dstColorSpace;
       
   823                 iccDstNumComp = dstNumComp;
       
   824             }
       
   825             float[] dstMinVal = new float[iccDstNumComp];
       
   826             float[] dstDiffMinMax = new float[iccDstNumComp];
       
   827             for (int i = 0; i < dstNumComp; i++) {
       
   828                 dstMinVal[i] = cs.getMinValue(i);
       
   829                 dstDiffMinMax[i] = (cs.getMaxValue(i) - dstMinVal[i]) / maxNum;
       
   830             }
       
   831             float[] dstColor;
       
   832             if (dstHasAlpha) {
       
   833                 int size = ((dstNumComp + 1) > 3) ? (dstNumComp + 1) : 3;
       
   834                 dstColor = new float[size];
       
   835             } else {
       
   836                 int size = (dstNumComp  > 3) ? dstNumComp : 3;
       
   837                 dstColor = new float[size];
       
   838             }
       
   839             short[] srcLine = new short[w * iccSrcNumComp];
       
   840             short[] dstLine = new short[w * iccDstNumComp];
       
   841             Object pixel;
       
   842             float[] color;
       
   843             float[] alpha = null;
       
   844             if (needSrcAlpha) {
       
   845                 alpha = new float[w];
       
   846             }
       
   847             int idx;
       
   848             // process each scanline
       
   849             for (int y = 0; y < h; y++) {
       
   850                 // convert src scanline
       
   851                 pixel = null;
       
   852                 color = null;
       
   853                 idx = 0;
       
   854                 for (int x = 0; x < w; x++) {
       
   855                     pixel = srcRas.getDataElements(x, y, pixel);
       
   856                     color = srcCM.getNormalizedComponents(pixel, color, 0);
       
   857                     if (needSrcAlpha) {
       
   858                         alpha[x] = color[srcNumComp];
       
   859                     }
       
   860                     if (nonICCSrc) {
       
   861                         color = srcColorSpace.toCIEXYZ(color);
       
   862                     }
       
   863                     for (int i = 0; i < iccSrcNumComp; i++) {
       
   864                         srcLine[idx++] = (short)
       
   865                             ((color[i] - srcMinVal[i]) * srcInvDiffMinMax[i] +
       
   866                              0.5f);
       
   867                     }
       
   868                 }
       
   869                 // color convert srcLine to dstLine
       
   870                 thisTransform.colorConvert(srcLine, dstLine);
       
   871                 // convert dst scanline
       
   872                 pixel = null;
       
   873                 idx = 0;
       
   874                 for (int x = 0; x < w; x++) {
       
   875                     for (int i = 0; i < iccDstNumComp; i++) {
       
   876                         dstColor[i] = ((float) (dstLine[idx++] & 0xffff)) *
       
   877                                       dstDiffMinMax[i] + dstMinVal[i];
       
   878                     }
       
   879                     if (nonICCDst) {
       
   880                         color = srcColorSpace.fromCIEXYZ(dstColor);
       
   881                         for (int i = 0; i < dstNumComp; i++) {
       
   882                             dstColor[i] = color[i];
       
   883                         }
       
   884                     }
       
   885                     if (needSrcAlpha) {
       
   886                         dstColor[dstNumComp] = alpha[x];
       
   887                     } else if (dstHasAlpha) {
       
   888                         dstColor[dstNumComp] = 1.0f;
       
   889                     }
       
   890                     pixel = dstCM.getDataElements(dstColor, 0, pixel);
       
   891                     dstRas.setDataElements(x, y, pixel);
       
   892                 }
       
   893             }
       
   894         } else {
       
   895             /* possible non-ICC src, possible CSList, possible non-ICC dst */
       
   896             // process per pixel
       
   897             int numCS;
       
   898             if (CSList == null) {
       
   899                 numCS = 0;
       
   900             } else {
       
   901                 numCS = CSList.length;
       
   902             }
       
   903             float[] dstColor;
       
   904             if (dstHasAlpha) {
       
   905                 dstColor = new float[dstNumComp + 1];
       
   906             } else {
       
   907                 dstColor = new float[dstNumComp];
       
   908             }
       
   909             Object spixel = null;
       
   910             Object dpixel = null;
       
   911             float[] color = null;
       
   912             float[] tmpColor;
       
   913             // process each pixel
       
   914             for (int y = 0; y < h; y++) {
       
   915                 for (int x = 0; x < w; x++) {
       
   916                     spixel = srcRas.getDataElements(x, y, spixel);
       
   917                     color = srcCM.getNormalizedComponents(spixel, color, 0);
       
   918                     tmpColor = srcColorSpace.toCIEXYZ(color);
       
   919                     for (int i = 0; i < numCS; i++) {
       
   920                         tmpColor = CSList[i].fromCIEXYZ(tmpColor);
       
   921                         tmpColor = CSList[i].toCIEXYZ(tmpColor);
       
   922                     }
       
   923                     tmpColor = dstColorSpace.fromCIEXYZ(tmpColor);
       
   924                     for (int i = 0; i < dstNumComp; i++) {
       
   925                         dstColor[i] = tmpColor[i];
       
   926                     }
       
   927                     if (needSrcAlpha) {
       
   928                         dstColor[dstNumComp] = color[srcNumComp];
       
   929                     } else if (dstHasAlpha) {
       
   930                         dstColor[dstNumComp] = 1.0f;
       
   931                     }
       
   932                     dpixel = dstCM.getDataElements(dstColor, 0, dpixel);
       
   933                     dstRas.setDataElements(x, y, dpixel);
       
   934 
       
   935                 }
       
   936             }
       
   937         }
       
   938 
       
   939         return dst;
       
   940     }
       
   941 
       
   942     /* color convert a Raster - handles byte, ushort, int, short, float,
       
   943        or double transferTypes */
       
   944     private final WritableRaster nonICCRasterFilter(Raster src,
       
   945                                                     WritableRaster dst)  {
       
   946 
       
   947         if (CSList.length != 2) {
       
   948             throw new IllegalArgumentException(
       
   949                 "Destination ColorSpace is undefined");
       
   950         }
       
   951         if (src.getNumBands() != CSList[0].getNumComponents()) {
       
   952             throw new IllegalArgumentException(
       
   953                 "Numbers of source Raster bands and source color space " +
       
   954                 "components do not match");
       
   955         }
       
   956         if (dst == null) {
       
   957             dst = createCompatibleDestRaster(src);
       
   958         } else {
       
   959             if (src.getHeight() != dst.getHeight() ||
       
   960                 src.getWidth() != dst.getWidth()) {
       
   961                 throw new IllegalArgumentException(
       
   962                     "Width or height of Rasters do not match");
       
   963             }
       
   964             if (dst.getNumBands() != CSList[1].getNumComponents()) {
       
   965                 throw new IllegalArgumentException(
       
   966                     "Numbers of destination Raster bands and destination " +
       
   967                     "color space components do not match");
       
   968             }
       
   969         }
       
   970 
       
   971         if (srcMinVals == null) {
       
   972             getMinMaxValsFromColorSpaces(CSList[0], CSList[1]);
       
   973         }
       
   974 
       
   975         SampleModel srcSM = src.getSampleModel();
       
   976         SampleModel dstSM = dst.getSampleModel();
       
   977         boolean srcIsFloat, dstIsFloat;
       
   978         int srcTransferType = src.getTransferType();
       
   979         int dstTransferType = dst.getTransferType();
       
   980         if ((srcTransferType == DataBuffer.TYPE_FLOAT) ||
       
   981             (srcTransferType == DataBuffer.TYPE_DOUBLE)) {
       
   982             srcIsFloat = true;
       
   983         } else {
       
   984             srcIsFloat = false;
       
   985         }
       
   986         if ((dstTransferType == DataBuffer.TYPE_FLOAT) ||
       
   987             (dstTransferType == DataBuffer.TYPE_DOUBLE)) {
       
   988             dstIsFloat = true;
       
   989         } else {
       
   990             dstIsFloat = false;
       
   991         }
       
   992         int w = src.getWidth();
       
   993         int h = src.getHeight();
       
   994         int srcNumBands = src.getNumBands();
       
   995         int dstNumBands = dst.getNumBands();
       
   996         float[] srcScaleFactor = null;
       
   997         float[] dstScaleFactor = null;
       
   998         if (!srcIsFloat) {
       
   999             srcScaleFactor = new float[srcNumBands];
       
  1000             for (int i = 0; i < srcNumBands; i++) {
       
  1001                 if (srcTransferType == DataBuffer.TYPE_SHORT) {
       
  1002                     srcScaleFactor[i] = (srcMaxVals[i] - srcMinVals[i]) /
       
  1003                                         32767.0f;
       
  1004                 } else {
       
  1005                     srcScaleFactor[i] = (srcMaxVals[i] - srcMinVals[i]) /
       
  1006                         ((float) ((1 << srcSM.getSampleSize(i)) - 1));
       
  1007                 }
       
  1008             }
       
  1009         }
       
  1010         if (!dstIsFloat) {
       
  1011             dstScaleFactor = new float[dstNumBands];
       
  1012             for (int i = 0; i < dstNumBands; i++) {
       
  1013                 if (dstTransferType == DataBuffer.TYPE_SHORT) {
       
  1014                     dstScaleFactor[i] = 32767.0f /
       
  1015                                         (dstMaxVals[i] - dstMinVals[i]);
       
  1016                 } else {
       
  1017                     dstScaleFactor[i] =
       
  1018                         ((float) ((1 << dstSM.getSampleSize(i)) - 1)) /
       
  1019                         (dstMaxVals[i] - dstMinVals[i]);
       
  1020                 }
       
  1021             }
       
  1022         }
       
  1023         int ys = src.getMinY();
       
  1024         int yd = dst.getMinY();
       
  1025         int xs, xd;
       
  1026         float sample;
       
  1027         float[] color = new float[srcNumBands];
       
  1028         float[] tmpColor;
       
  1029         ColorSpace srcColorSpace = CSList[0];
       
  1030         ColorSpace dstColorSpace = CSList[1];
       
  1031         // process each pixel
       
  1032         for (int y = 0; y < h; y++, ys++, yd++) {
       
  1033             // get src scanline
       
  1034             xs = src.getMinX();
       
  1035             xd = dst.getMinX();
       
  1036             for (int x = 0; x < w; x++, xs++, xd++) {
       
  1037                 for (int i = 0; i < srcNumBands; i++) {
       
  1038                     sample = src.getSampleFloat(xs, ys, i);
       
  1039                     if (!srcIsFloat) {
       
  1040                         sample = sample * srcScaleFactor[i] + srcMinVals[i];
       
  1041                     }
       
  1042                     color[i] = sample;
       
  1043                 }
       
  1044                 tmpColor = srcColorSpace.toCIEXYZ(color);
       
  1045                 tmpColor = dstColorSpace.fromCIEXYZ(tmpColor);
       
  1046                 for (int i = 0; i < dstNumBands; i++) {
       
  1047                     sample = tmpColor[i];
       
  1048                     if (!dstIsFloat) {
       
  1049                         sample = (sample - dstMinVals[i]) * dstScaleFactor[i];
       
  1050                     }
       
  1051                     dst.setSample(xd, yd, i, sample);
       
  1052                 }
       
  1053             }
       
  1054         }
       
  1055         return dst;
       
  1056     }
       
  1057 
       
  1058     private void getMinMaxValsFromProfiles(ICC_Profile srcProfile,
       
  1059                                            ICC_Profile dstProfile) {
       
  1060         int type = srcProfile.getColorSpaceType();
       
  1061         int nc = srcProfile.getNumComponents();
       
  1062         srcMinVals = new float[nc];
       
  1063         srcMaxVals = new float[nc];
       
  1064         setMinMax(type, nc, srcMinVals, srcMaxVals);
       
  1065         type = dstProfile.getColorSpaceType();
       
  1066         nc = dstProfile.getNumComponents();
       
  1067         dstMinVals = new float[nc];
       
  1068         dstMaxVals = new float[nc];
       
  1069         setMinMax(type, nc, dstMinVals, dstMaxVals);
       
  1070     }
       
  1071 
       
  1072     private void setMinMax(int type, int nc, float[] minVals, float[] maxVals) {
       
  1073         if (type == ColorSpace.TYPE_Lab) {
       
  1074             minVals[0] = 0.0f;    // L
       
  1075             maxVals[0] = 100.0f;
       
  1076             minVals[1] = -128.0f; // a
       
  1077             maxVals[1] = 127.0f;
       
  1078             minVals[2] = -128.0f; // b
       
  1079             maxVals[2] = 127.0f;
       
  1080         } else if (type == ColorSpace.TYPE_XYZ) {
       
  1081             minVals[0] = minVals[1] = minVals[2] = 0.0f; // X, Y, Z
       
  1082             maxVals[0] = maxVals[1] = maxVals[2] = 1.0f + (32767.0f/ 32768.0f);
       
  1083         } else {
       
  1084             for (int i = 0; i < nc; i++) {
       
  1085                 minVals[i] = 0.0f;
       
  1086                 maxVals[i] = 1.0f;
       
  1087             }
       
  1088         }
       
  1089     }
       
  1090 
       
  1091     private void getMinMaxValsFromColorSpaces(ColorSpace srcCspace,
       
  1092                                               ColorSpace dstCspace) {
       
  1093         int nc = srcCspace.getNumComponents();
       
  1094         srcMinVals = new float[nc];
       
  1095         srcMaxVals = new float[nc];
       
  1096         for (int i = 0; i < nc; i++) {
       
  1097             srcMinVals[i] = srcCspace.getMinValue(i);
       
  1098             srcMaxVals[i] = srcCspace.getMaxValue(i);
       
  1099         }
       
  1100         nc = dstCspace.getNumComponents();
       
  1101         dstMinVals = new float[nc];
       
  1102         dstMaxVals = new float[nc];
       
  1103         for (int i = 0; i < nc; i++) {
       
  1104             dstMinVals[i] = dstCspace.getMinValue(i);
       
  1105             dstMaxVals[i] = dstCspace.getMaxValue(i);
       
  1106         }
       
  1107     }
       
  1108 
       
  1109 }