Source for gnu.java.awt.color.RgbProfileConverter

   1: /* RgbProfileConverter.java -- RGB Profile conversion class
   2:    Copyright (C) 2004 Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.awt.color;
  40: 
  41: import java.awt.color.ICC_Profile;
  42: import java.awt.color.ICC_ProfileRGB;
  43: import java.awt.color.ProfileDataException;
  44: 
  45: /**
  46:  * RgbProfileConverter - converts RGB profiles (ICC_ProfileRGB)
  47:  *
  48:  * This type of profile contains a matrix and three
  49:  * tone reproduction curves (TRCs).
  50:  *
  51:  * Device RGB --> CIE XYZ is done through first multiplying with
  52:  * a matrix, then each component is looked-up against it's TRC.
  53:  *
  54:  * The opposite transform is done using the inverse of the matrix,
  55:  * and TRC:s.
  56:  *
  57:  * @author Sven de Marothy
  58:  */
  59: public class RgbProfileConverter implements ColorSpaceConverter
  60: {
  61:   private float[][] matrix;
  62:   private float[][] inv_matrix;
  63:   private ToneReproductionCurve rTRC;
  64:   private ToneReproductionCurve gTRC;
  65:   private ToneReproductionCurve bTRC;
  66:   private ColorLookUpTable toPCS;
  67:   private ColorLookUpTable fromPCS;
  68: 
  69:   /**
  70:    * CIE 1931 D50 white point (in Lab coordinates)
  71:    */
  72:   private static float[] D50 = { 0.96422f, 1.00f, 0.82521f };
  73: 
  74:   /**
  75:    * Constructs an RgbProfileConverter from a given ICC_ProfileRGB
  76:    */
  77:   public RgbProfileConverter(ICC_ProfileRGB profile)
  78:   {
  79:     toPCS = fromPCS = null;
  80:     matrix = profile.getMatrix();
  81: 
  82:     // get TRCs
  83:     try
  84:       {
  85:         rTRC = new ToneReproductionCurve(profile.getGamma(ICC_ProfileRGB.REDCOMPONENT));
  86:       }
  87:     catch (ProfileDataException e)
  88:       {
  89:         rTRC = new ToneReproductionCurve(profile.getTRC(ICC_ProfileRGB.REDCOMPONENT));
  90:       }
  91:     try
  92:       {
  93:         gTRC = new ToneReproductionCurve(profile.getGamma(ICC_ProfileRGB.GREENCOMPONENT));
  94:       }
  95:     catch (ProfileDataException e)
  96:       {
  97:         gTRC = new ToneReproductionCurve(profile.getTRC(ICC_ProfileRGB.GREENCOMPONENT));
  98:       }
  99:     try
 100:       {
 101:         bTRC = new ToneReproductionCurve(profile.getGamma(ICC_ProfileRGB.BLUECOMPONENT));
 102:       }
 103:     catch (ProfileDataException e)
 104:       {
 105:         bTRC = new ToneReproductionCurve(profile.getTRC(ICC_ProfileRGB.BLUECOMPONENT));
 106:       }
 107: 
 108:     // If a CLUT is available, it should be used, and the TRCs ignored.
 109:     // Note: A valid profile may only have CLUTs in one direction, and
 110:     // TRC:s without useful info, making reverse-transforms impossible.
 111:     // In this case the TRC will be used for the reverse-transform with
 112:     // unpredictable results. This is in line with the Java specification,
 113:     try
 114:       {
 115:         toPCS = new ColorLookUpTable(profile, ICC_Profile.icSigAToB0Tag);
 116:       }
 117:     catch (Exception e)
 118:       {
 119:         toPCS = null;
 120:       }
 121: 
 122:     try
 123:       {
 124:         fromPCS = new ColorLookUpTable(profile, ICC_Profile.icSigBToA0Tag);
 125:       }
 126:     catch (Exception e)
 127:       {
 128:         fromPCS = null;
 129:       }
 130: 
 131:     // Calculate the inverse matrix if no reverse CLUT is available
 132:     if(fromPCS == null)
 133:         inv_matrix = invertMatrix(matrix);
 134:     else
 135:       {
 136:         // otherwise just set it to an identity matrix
 137:         inv_matrix = new float[3][3];
 138:         inv_matrix[0][0] = inv_matrix[1][1] = inv_matrix[2][2] = 1.0f;
 139:       }
 140:   }
 141: 
 142:   public float[] toCIEXYZ(float[] in)
 143:   {
 144:     // CLUT takes precedence
 145:     if (toPCS != null)
 146:       return toPCS.lookup(in);
 147: 
 148:     float[] temp = new float[3];
 149:     float[] out = new float[3];
 150: 
 151:     // device space --> linear gamma
 152:     temp[0] = rTRC.lookup(in[0]);
 153:     temp[1] = gTRC.lookup(in[1]);
 154:     temp[2] = bTRC.lookup(in[2]);
 155: 
 156:     // matrix multiplication
 157:     out[0] = matrix[0][0] * temp[0] + matrix[0][1] * temp[1]
 158:              + matrix[0][2] * temp[2];
 159:     out[1] = matrix[1][0] * temp[0] + matrix[1][1] * temp[1]
 160:              + matrix[1][2] * temp[2];
 161:     out[2] = matrix[2][0] * temp[0] + matrix[2][1] * temp[1]
 162:              + matrix[2][2] * temp[2];
 163: 
 164:     return out;
 165:   }
 166: 
 167:   public float[] toRGB(float[] in)
 168:   {
 169:     return SrgbConverter.XYZtoRGB(toCIEXYZ(in));
 170:   }
 171: 
 172:   public float[] fromCIEXYZ(float[] in)
 173:   {
 174:     if (fromPCS != null)
 175:       return fromPCS.lookup(in);
 176: 
 177:     float[] temp = new float[3];
 178:     float[] out = new float[3];
 179: 
 180:     // matrix multiplication
 181:     temp[0] = inv_matrix[0][0] * in[0] + inv_matrix[0][1] * in[1]
 182:               + inv_matrix[0][2] * in[2];
 183:     temp[1] = inv_matrix[1][0] * in[0] + inv_matrix[1][1] * in[1]
 184:               + inv_matrix[1][2] * in[2];
 185:     temp[2] = inv_matrix[2][0] * in[0] + inv_matrix[2][1] * in[1]
 186:               + inv_matrix[2][2] * in[2];
 187: 
 188:     // device space --> linear gamma
 189:     out[0] = rTRC.reverseLookup(temp[0]);
 190:     out[1] = gTRC.reverseLookup(temp[1]);
 191:     out[2] = bTRC.reverseLookup(temp[2]);
 192: 
 193:     // FIXME: Sun appears to clip the return values to [0,1]
 194:     // I don't believe that is a Good Thing,
 195:     // (some colorspaces may allow values outside that range.)
 196:     // So we return the actual values here.
 197:     return out;
 198:   }
 199: 
 200:   public float[] fromRGB(float[] in)
 201:   {
 202:     return fromCIEXYZ(SrgbConverter.RGBtoXYZ(in));
 203:   }
 204: 
 205:   /**
 206:    * Inverts a 3x3 matrix, returns the inverse,
 207:    * throws an IllegalArgumentException if the matrix is not
 208:    * invertible (this shouldn't happen for a valid profile)
 209:    */
 210:   private float[][] invertMatrix(float[][] matrix)
 211:   {
 212:     float[][] out = new float[3][3];
 213:     double determinant = matrix[0][0] * (matrix[1][1] * matrix[2][2]
 214:                          - matrix[2][1] * matrix[1][2])
 215:                          - matrix[0][1] * (matrix[1][0] * matrix[2][2]
 216:                          - matrix[2][0] * matrix[1][2])
 217:                          + matrix[0][2] * (matrix[1][0] * matrix[2][1]
 218:                          - matrix[2][0] * matrix[1][1]);
 219: 
 220:     if (determinant == 0.0)
 221:       throw new IllegalArgumentException("Can't invert conversion matrix.");
 222:     float invdet = (float) (1.0 / determinant);
 223: 
 224:     out[0][0] = invdet * (matrix[1][1] * matrix[2][2]
 225:                 - matrix[1][2] * matrix[2][1]);
 226:     out[0][1] = invdet * (matrix[0][2] * matrix[2][1]
 227:                 - matrix[0][1] * matrix[2][2]);
 228:     out[0][2] = invdet * (matrix[0][1] * matrix[1][2]
 229:                 - matrix[0][2] * matrix[1][1]);
 230:     out[1][0] = invdet * (matrix[1][2] * matrix[2][0]
 231:                 - matrix[1][0] * matrix[2][2]);
 232:     out[1][1] = invdet * (matrix[0][0] * matrix[2][2]
 233:                 - matrix[0][2] * matrix[2][0]);
 234:     out[1][2] = invdet * (matrix[0][2] * matrix[1][0]
 235:                 - matrix[0][0] * matrix[1][2]);
 236:     out[2][0] = invdet * (matrix[1][0] * matrix[2][1]
 237:                 - matrix[1][1] * matrix[2][0]);
 238:     out[2][1] = invdet * (matrix[0][1] * matrix[2][0]
 239:                 - matrix[0][0] * matrix[2][1]);
 240:     out[2][2] = invdet * (matrix[0][0] * matrix[1][1]
 241:                 - matrix[0][1] * matrix[1][0]);
 242:     return out;
 243:   }
 244: }