Source for java.awt.color.ICC_Profile

   1: /* ICC_Profile.java -- color space profiling
   2:    Copyright (C) 2000, 2002, 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 java.awt.color;
  40: 
  41: import gnu.java.awt.color.ProfileHeader;
  42: import gnu.java.awt.color.TagEntry;
  43: 
  44: import java.io.FileInputStream;
  45: import java.io.FileOutputStream;
  46: import java.io.IOException;
  47: import java.io.InputStream;
  48: import java.io.ObjectInputStream;
  49: import java.io.ObjectOutputStream;
  50: import java.io.ObjectStreamException;
  51: import java.io.OutputStream;
  52: import java.io.Serializable;
  53: import java.io.UnsupportedEncodingException;
  54: import java.nio.ByteBuffer;
  55: import java.util.Enumeration;
  56: import java.util.Hashtable;
  57: 
  58: /**
  59:  * ICC Profile - represents an ICC Color profile.
  60:  * The ICC profile format is a standard file format which maps the transform
  61:  * from a device color space to a standard Profile Color Space (PCS), which
  62:  * can either be CIE L*a*b or CIE XYZ.
  63:  * (With the exception of device link profiles which map from one device space
  64:  * to another)
  65:  *
  66:  * ICC profiles calibrated to specific input/output devices are used when color
  67:  * fidelity is of importance.
  68:  *
  69:  * An instance of ICC_Profile can be created using the getInstance() methods,
  70:  * either using one of the predefined color spaces enumerated in ColorSpace,
  71:  * or from an ICC profile file, or from an input stream.
  72:  *
  73:  * An ICC_ColorSpace object can then be created to transform color values
  74:  * through the profile.
  75:  *
  76:  * The ICC_Profile class implements the version 2 format specified by
  77:  * International Color Consortium Specification ICC.1:1998-09,
  78:  * and its addendum ICC.1A:1999-04, April 1999
  79:  * (available at www.color.org)
  80:  *
  81:  * @author Sven de Marothy
  82:  * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
  83:  * @since 1.2
  84:  */
  85: public class ICC_Profile implements Serializable
  86: {
  87:   /**
  88:    * Compatible with JDK 1.2+.
  89:    */
  90:   private static final long serialVersionUID = -3938515861990936766L;
  91: 
  92:   /**
  93:    * ICC Profile classes
  94:    */
  95:   public static final int CLASS_INPUT = 0;
  96:   public static final int CLASS_DISPLAY = 1;
  97:   public static final int CLASS_OUTPUT = 2;
  98:   public static final int CLASS_DEVICELINK = 3;
  99:   public static final int CLASS_COLORSPACECONVERSION = 4;
 100:   public static final int CLASS_ABSTRACT = 5;
 101:   public static final int CLASS_NAMEDCOLOR = 6;
 102: 
 103:   /**
 104:    * ICC Profile class signatures
 105:    */
 106:   public static final int icSigInputClass = 0x73636e72; // 'scnr'
 107:   public static final int icSigDisplayClass = 0x6d6e7472; // 'mntr'
 108:   public static final int icSigOutputClass = 0x70727472; // 'prtr'
 109:   public static final int icSigLinkClass = 0x6c696e6b; // 'link'
 110:   public static final int icSigColorSpaceClass = 0x73706163; // 'spac'
 111:   public static final int icSigAbstractClass = 0x61627374; // 'abst'
 112:   public static final int icSigNamedColorClass = 0x6e6d636c; // 'nmcl'
 113: 
 114:   /**
 115:    * Color space signatures
 116:    */
 117:   public static final int icSigXYZData = 0x58595A20; // 'XYZ '
 118:   public static final int icSigLabData = 0x4C616220; // 'Lab '
 119:   public static final int icSigLuvData = 0x4C757620; // 'Luv '
 120:   public static final int icSigYCbCrData = 0x59436272; // 'YCbr'
 121:   public static final int icSigYxyData = 0x59787920; // 'Yxy '
 122:   public static final int icSigRgbData = 0x52474220; // 'RGB '
 123:   public static final int icSigGrayData = 0x47524159; // 'GRAY'
 124:   public static final int icSigHsvData = 0x48535620; // 'HSV '
 125:   public static final int icSigHlsData = 0x484C5320; // 'HLS '
 126:   public static final int icSigCmykData = 0x434D594B; // 'CMYK'
 127:   public static final int icSigCmyData = 0x434D5920; // 'CMY '
 128:   public static final int icSigSpace2CLR = 0x32434C52; // '2CLR'
 129:   public static final int icSigSpace3CLR = 0x33434C52; // '3CLR'
 130:   public static final int icSigSpace4CLR = 0x34434C52; // '4CLR'
 131:   public static final int icSigSpace5CLR = 0x35434C52; // '5CLR'
 132:   public static final int icSigSpace6CLR = 0x36434C52; // '6CLR'
 133:   public static final int icSigSpace7CLR = 0x37434C52; // '7CLR'
 134:   public static final int icSigSpace8CLR = 0x38434C52; // '8CLR'
 135:   public static final int icSigSpace9CLR = 0x39434C52; // '9CLR'
 136:   public static final int icSigSpaceACLR = 0x41434C52; // 'ACLR'
 137:   public static final int icSigSpaceBCLR = 0x42434C52; // 'BCLR'
 138:   public static final int icSigSpaceCCLR = 0x43434C52; // 'CCLR'
 139:   public static final int icSigSpaceDCLR = 0x44434C52; // 'DCLR'
 140:   public static final int icSigSpaceECLR = 0x45434C52; // 'ECLR'
 141:   public static final int icSigSpaceFCLR = 0x46434C52; // 'FCLR'
 142: 
 143:   /**
 144:    * Rendering intents
 145:    */
 146:   public static final int icPerceptual = 0;
 147:   public static final int icRelativeColorimetric = 1;
 148:   public static final int icSaturation = 2;
 149:   public static final int icAbsoluteColorimetric = 3;
 150: 
 151:   /**
 152:    * Tag signatures
 153:    */
 154:   public static final int icSigAToB0Tag = 0x41324230; // 'A2B0'
 155:   public static final int icSigAToB1Tag = 0x41324231; // 'A2B1'
 156:   public static final int icSigAToB2Tag = 0x41324232; // 'A2B2'
 157:   public static final int icSigBlueColorantTag = 0x6258595A; // 'bXYZ'
 158:   public static final int icSigBlueTRCTag = 0x62545243; // 'bTRC'
 159:   public static final int icSigBToA0Tag = 0x42324130; // 'B2A0'
 160:   public static final int icSigBToA1Tag = 0x42324131; // 'B2A1'
 161:   public static final int icSigBToA2Tag = 0x42324132; // 'B2A2'
 162:   public static final int icSigCalibrationDateTimeTag = 0x63616C74; // 'calt'
 163:   public static final int icSigCharTargetTag = 0x74617267; // 'targ'
 164:   public static final int icSigCopyrightTag = 0x63707274; // 'cprt'
 165:   public static final int icSigCrdInfoTag = 0x63726469; // 'crdi'
 166:   public static final int icSigDeviceMfgDescTag = 0x646D6E64; // 'dmnd'
 167:   public static final int icSigDeviceModelDescTag = 0x646D6464; // 'dmdd'
 168:   public static final int icSigDeviceSettingsTag = 0x64657673; // 'devs'
 169:   public static final int icSigGamutTag = 0x67616D74; // 'gamt'
 170:   public static final int icSigGrayTRCTag = 0x6b545243; // 'kTRC'
 171:   public static final int icSigGreenColorantTag = 0x6758595A; // 'gXYZ'
 172:   public static final int icSigGreenTRCTag = 0x67545243; // 'gTRC'
 173:   public static final int icSigLuminanceTag = 0x6C756d69; // 'lumi'
 174:   public static final int icSigMeasurementTag = 0x6D656173; // 'meas'
 175:   public static final int icSigMediaBlackPointTag = 0x626B7074; // 'bkpt'
 176:   public static final int icSigMediaWhitePointTag = 0x77747074; // 'wtpt'
 177:   public static final int icSigNamedColor2Tag = 0x6E636C32; // 'ncl2'
 178:   public static final int icSigOutputResponseTag = 0x72657370; // 'resp'
 179:   public static final int icSigPreview0Tag = 0x70726530; // 'pre0'
 180:   public static final int icSigPreview1Tag = 0x70726531; // 'pre1'
 181:   public static final int icSigPreview2Tag = 0x70726532; // 'pre2'
 182:   public static final int icSigProfileDescriptionTag = 0x64657363; // 'desc'
 183:   public static final int icSigProfileSequenceDescTag = 0x70736571; // 'pseq'
 184:   public static final int icSigPs2CRD0Tag = 0x70736430; // 'psd0'
 185:   public static final int icSigPs2CRD1Tag = 0x70736431; // 'psd1'
 186:   public static final int icSigPs2CRD2Tag = 0x70736432; // 'psd2'
 187:   public static final int icSigPs2CRD3Tag = 0x70736433; // 'psd3'
 188:   public static final int icSigPs2CSATag = 0x70733273; // 'ps2s'
 189:   public static final int icSigPs2RenderingIntentTag = 0x70733269; // 'ps2i'
 190:   public static final int icSigRedColorantTag = 0x7258595A; // 'rXYZ'
 191:   public static final int icSigRedTRCTag = 0x72545243; // 'rTRC'
 192:   public static final int icSigScreeningDescTag = 0x73637264; // 'scrd'
 193:   public static final int icSigScreeningTag = 0x7363726E; // 'scrn'
 194:   public static final int icSigTechnologyTag = 0x74656368; // 'tech'
 195:   public static final int icSigUcrBgTag = 0x62666420; // 'bfd '
 196:   public static final int icSigViewingCondDescTag = 0x76756564; // 'vued'
 197:   public static final int icSigViewingConditionsTag = 0x76696577; // 'view'
 198:   public static final int icSigChromaticityTag = 0x6368726D; // 'chrm'
 199: 
 200:   /**
 201:    * Non-ICC tag 'head' for use in retrieving the header with getData()
 202:    */
 203:   public static final int icSigHead = 0x68656164;
 204: 
 205:   /**
 206:    * Header offsets
 207:    */
 208:   public static final int icHdrSize = 0;
 209:   public static final int icHdrCmmId = 4;
 210:   public static final int icHdrVersion = 8;
 211:   public static final int icHdrDeviceClass = 12;
 212:   public static final int icHdrColorSpace = 16;
 213:   public static final int icHdrPcs = 20;
 214:   public static final int icHdrDate = 24;
 215:   public static final int icHdrMagic = 36;
 216:   public static final int icHdrPlatform = 40;
 217:   public static final int icHdrFlags = 44;
 218:   public static final int icHdrManufacturer = 48;
 219:   public static final int icHdrModel = 52;
 220:   public static final int icHdrAttributes = 56;
 221:   public static final int icHdrRenderingIntent = 64;
 222:   public static final int icHdrIlluminant = 68;
 223:   public static final int icHdrCreator = 80;
 224: 
 225:   /**
 226:    *
 227:    */
 228:   public static final int icTagType = 0;
 229:   public static final int icTagReserved = 4;
 230:   public static final int icCurveCount = 8;
 231:   public static final int icCurveData = 12;
 232:   public static final int icXYZNumberX = 8;
 233: 
 234:   /**
 235:    * offset of the Tag table
 236:    */
 237:   private static final int tagTableOffset = 128;
 238: 
 239:   /**
 240:    * @serial
 241:    */
 242:   private static final int iccProfileSerializedDataVersion = 1;
 243: 
 244:   /**
 245:    * Constants related to generating profiles for
 246:    * built-in colorspace profiles
 247:    */
 248:   /**
 249:    * Copyright notice to stick into built-in-profile files.
 250:    */
 251:   private static final String copyrightNotice = "Generated by GNU Classpath.";
 252: 
 253:   /**
 254:    * Resolution of the TRC to use for predefined profiles.
 255:    * 1024 should suffice.
 256:    */
 257:   private static final int TRC_POINTS = 1024;
 258: 
 259:   /**
 260:    * CIE 1931 D50 white point (in Lab coordinates)
 261:    */
 262:   private static final float[] D50 = { 0.96422f, 1.00f, 0.82521f };
 263: 
 264:   /**
 265:    * Color space profile ID
 266:    * Set to the predefined profile class (e.g. CS_sRGB) if a predefined
 267:    * color space is used, set to -1 otherwise.
 268:    * (or if the profile has been modified)
 269:    */
 270:   private transient int profileID;
 271: 
 272:   /**
 273:    * The profile header data
 274:    */
 275:   private transient ProfileHeader header;
 276: 
 277:   /**
 278:    * A hashtable containing the profile tags as TagEntry objects
 279:    */
 280:   private transient Hashtable tagTable;
 281: 
 282:   /**
 283:    * Contructor for predefined colorspaces
 284:    */
 285:   ICC_Profile(int profileID)
 286:   {
 287:     header = null;
 288:     tagTable = null;
 289:     createProfile(profileID);
 290:   }
 291: 
 292:   /**
 293:    * Constructs an ICC_Profile from a header and a table of loaded tags.
 294:    */
 295:   ICC_Profile(ProfileHeader h, Hashtable tags) throws IllegalArgumentException
 296:   {
 297:     header = h;
 298:     tagTable = tags;
 299:     profileID = -1; // Not a predefined color space
 300:   }
 301: 
 302:   /**
 303:    * Constructs an ICC_Profile from a byte array of data.
 304:    */
 305:   ICC_Profile(byte[] data) throws IllegalArgumentException
 306:   {
 307:     // get header and verify it
 308:     header = new ProfileHeader(data);
 309:     header.verifyHeader(data.length);
 310:     tagTable = createTagTable(data);
 311:     profileID = -1; // Not a predefined color space
 312:   }
 313: 
 314:   /**
 315:    * Free up the used memory.
 316:    */
 317:   protected void finalize()
 318:   {
 319:   }
 320: 
 321:   /**
 322:    * Returns an ICC_Profile instance from a byte array of profile data.
 323:    *
 324:    * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
 325:    * may be returned if appropriate.
 326:    *
 327:    * @param data - the profile data
 328:    * @return An ICC_Profile object
 329:    *
 330:    * @throws IllegalArgumentException if the profile data is an invalid
 331:    * v2 profile.
 332:    */
 333:   public static ICC_Profile getInstance(byte[] data)
 334:   {
 335:     ProfileHeader header = new ProfileHeader(data);
 336: 
 337:     // verify it as a correct ICC header, including size
 338:     header.verifyHeader(data.length);
 339: 
 340:     Hashtable tags = createTagTable(data);
 341: 
 342:     if (isRGBProfile(header, tags))
 343:       return new ICC_ProfileRGB(data);
 344:     if (isGrayProfile(header, tags))
 345:       return new ICC_ProfileGray(data);
 346: 
 347:     return new ICC_Profile(header, tags);
 348:   }
 349: 
 350:   /**
 351:    * Returns an predefined ICC_Profile instance.
 352:    *
 353:    * This will construct an ICC_Profile instance from one of the predefined
 354:    * color spaces in the ColorSpace class. (e.g. CS_sRGB, CS_GRAY, etc)
 355:    *
 356:    * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
 357:    * may be returned if appropriate.
 358:    *
 359:    * @return An ICC_Profile object
 360:    */
 361:   public static ICC_Profile getInstance(int cspace)
 362:   {
 363:     if (cspace == ColorSpace.CS_sRGB || cspace == ColorSpace.CS_LINEAR_RGB)
 364:       return new ICC_ProfileRGB(cspace);
 365:     if (cspace == ColorSpace.CS_GRAY)
 366:       return new ICC_ProfileGray(cspace);
 367:     return new ICC_Profile(cspace);
 368:   }
 369: 
 370:   /**
 371:    * Returns an ICC_Profile instance from an ICC Profile file.
 372:    *
 373:    * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
 374:    * may be returned if appropriate.
 375:    *
 376:    * @param filename - the file name of the profile file.
 377:    * @return An ICC_Profile object
 378:    *
 379:    * @throws IllegalArgumentException if the profile data is an invalid
 380:    * v2 profile.
 381:    * @throws IOException if the file could not be read.
 382:    */
 383:   public static ICC_Profile getInstance(String filename)
 384:                                  throws IOException
 385:   {
 386:     return getInstance(new FileInputStream(filename));
 387:   }
 388: 
 389:   /**
 390:    * Returns an ICC_Profile instance from an InputStream.
 391:    *
 392:    * This method can be used for reading ICC profiles embedded in files
 393:    * which support this. (JPEG and SVG for instance).
 394:    *
 395:    * The stream is treated in the following way: The profile header
 396:    * (128 bytes) is read first, and the header is validated. If the profile
 397:    * header is valid, it will then attempt to read the rest of the profile
 398:    * from the stream. The stream is not closed after reading.
 399:    *
 400:    * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
 401:    * may be returned if appropriate.
 402:    *
 403:    * @param in - the input stream to read the profile from.
 404:    * @return An ICC_Profile object
 405:    *
 406:    * @throws IllegalArgumentException if the profile data is an invalid
 407:    * v2 profile.
 408:    * @throws IOException if the stream could not be read.
 409:    */
 410:   public static ICC_Profile getInstance(InputStream in)
 411:                                  throws IOException
 412:   {
 413:     // read the header
 414:     byte[] headerData = new byte[ProfileHeader.HEADERSIZE];
 415:     if (in.read(headerData) != ProfileHeader.HEADERSIZE)
 416:       throw new IllegalArgumentException("Invalid profile header");
 417: 
 418:     ProfileHeader header = new ProfileHeader(headerData);
 419: 
 420:     // verify it as a correct ICC header, but do not verify the
 421:     // size as we are reading from a stream.
 422:     header.verifyHeader(-1);
 423: 
 424:     // get the size
 425:     byte[] data = new byte[header.getSize()];
 426:     System.arraycopy(headerData, 0, data, 0, ProfileHeader.HEADERSIZE);
 427: 
 428:     // read the rest
 429:     int totalBytes = header.getSize() - ProfileHeader.HEADERSIZE;
 430:     int bytesLeft = totalBytes;
 431:     while (bytesLeft > 0)
 432:       {
 433:         int read = in.read(data,
 434:                            ProfileHeader.HEADERSIZE + (totalBytes - bytesLeft),
 435:                            bytesLeft);
 436:         bytesLeft -= read;
 437:       }
 438: 
 439:     return getInstance(data);
 440:   }
 441: 
 442:   /**
 443:    * Returns the major version number
 444:    */
 445:   public int getMajorVersion()
 446:   {
 447:     return header.getMajorVersion();
 448:   }
 449: 
 450:   /**
 451:    * Returns the minor version number.
 452:    *
 453:    * Only the least-significant byte contains data, in BCD form:
 454:    * the least-significant nibble is the BCD bug fix revision,
 455:    * the most-significant nibble is the BCD minor revision number.
 456:    *
 457:    * (E.g. For a v2.1.0 profile this will return <code>0x10</code>)
 458:    */
 459:   public int getMinorVersion()
 460:   {
 461:     return header.getMinorVersion();
 462:   }
 463: 
 464:   /**
 465:    * Returns the device class of this profile,
 466:    *
 467:    * (E.g. CLASS_INPUT for a scanner profile,
 468:    * CLASS_OUTPUT for a printer)
 469:    */
 470:   public int getProfileClass()
 471:   {
 472:     return header.getProfileClass();
 473:   }
 474: 
 475:   /**
 476:    * Returns the color space of this profile, in terms
 477:    * of the color space constants defined in ColorSpace.
 478:    * (For example, it may be a ColorSpace.TYPE_RGB)
 479:    */
 480:   public int getColorSpaceType()
 481:   {
 482:     return header.getColorSpace();
 483:   }
 484: 
 485:   /**
 486:    * Returns the color space of this profile's Profile Connection Space (OCS)
 487:    *
 488:    * In terms of the color space constants defined in ColorSpace.
 489:    * This may be TYPE_XYZ or TYPE_Lab
 490:    */
 491:   public int getPCSType()
 492:   {
 493:     return header.getProfileColorSpace();
 494:   }
 495: 
 496:   /**
 497:    * Writes the profile data to an ICC profile file.
 498:    * @param filename - The name of the file to write
 499:    * @throws IOException if the write failed.
 500:    */
 501:   public void write(String filename) throws IOException
 502:   {
 503:     FileOutputStream out = new FileOutputStream(filename);
 504:     write(out);
 505:     out.flush();
 506:     out.close();
 507:   }
 508: 
 509:   /**
 510:    * Writes the profile data in ICC profile file-format to a stream.
 511:    * This is useful for embedding ICC profiles in file formats which
 512:    * support this (such as JPEG and SVG).
 513:    *
 514:    * The stream is not closed after writing.
 515:    * @param out - The outputstream to which the profile data should be written
 516:    * @throws IOException if the write failed.
 517:    */
 518:   public void write(OutputStream out) throws IOException
 519:   {
 520:     out.write(getData());
 521:   }
 522: 
 523:   /**
 524:    * Returns the data corresponding to this ICC_Profile as a byte array.
 525:    *
 526:    * @return The data in a byte array,
 527:    * where the first element corresponds to first byte of the profile file.
 528:    */
 529:   public byte[] getData()
 530:   {
 531:     int size = getSize();
 532:     byte[] data = new byte[size];
 533: 
 534:     // Header
 535:     System.arraycopy(header.getData(size), 0, data, 0, ProfileHeader.HEADERSIZE);
 536:     // # of tags
 537:     byte[] tt = getTagTable();
 538:     System.arraycopy(tt, 0, data, ProfileHeader.HEADERSIZE, tt.length);
 539: 
 540:     Enumeration e = tagTable.elements();
 541:     while (e.hasMoreElements())
 542:       {
 543:         TagEntry tag = (TagEntry) e.nextElement();
 544:         System.arraycopy(tag.getData(), 0,
 545:                          data, tag.getOffset(), tag.getSize());
 546:       }
 547:     return data;
 548:   }
 549: 
 550:   /**
 551:    * Returns the ICC profile tag data
 552:    * The non ICC-tag icSigHead is also permitted to request the header data.
 553:    *
 554:    * @param tagSignature The ICC signature of the requested tag
 555:    * @return A byte array containing the tag data
 556:    */
 557:   public byte[] getData(int tagSignature)
 558:   {
 559:     if (tagSignature == icSigHead)
 560:       return header.getData(getSize());
 561: 
 562:     TagEntry t = (TagEntry) tagTable.get(TagEntry.tagHashKey(tagSignature));
 563:     if (t == null)
 564:       return null;
 565:     return t.getData();
 566:   }
 567: 
 568:   /**
 569:    * Sets the ICC profile tag data.
 570:    *
 571:    * Note that an ICC profile can only contain one tag of each type, if
 572:    * a tag already exists with the given signature, it is replaced.
 573:    *
 574:    * @param tagSignature - The signature of the tag to set
 575:    * @param data - A byte array containing the tag data
 576:    */
 577:   public void setData(int tagSignature, byte[] data)
 578:   {
 579:     profileID = -1; // Not a predefined color space if modified.
 580: 
 581:     if (tagSignature == icSigHead)
 582:       header = new ProfileHeader(data);
 583:     else
 584:       {
 585:         TagEntry t = new TagEntry(tagSignature, data);
 586:         tagTable.put(t.hashKey(), t);
 587:       }
 588:   }
 589: 
 590:   /**
 591:    * Get the number of components in the profile's device color space.
 592:    */
 593:   public int getNumComponents()
 594:   {
 595:     int[] lookup =
 596:                    {
 597:                      ColorSpace.TYPE_RGB, 3, ColorSpace.TYPE_CMY, 3,
 598:                      ColorSpace.TYPE_CMYK, 4, ColorSpace.TYPE_GRAY, 1,
 599:                      ColorSpace.TYPE_YCbCr, 3, ColorSpace.TYPE_XYZ, 3,
 600:                      ColorSpace.TYPE_Lab, 3, ColorSpace.TYPE_HSV, 3,
 601:                      ColorSpace.TYPE_2CLR, 2, ColorSpace.TYPE_Luv, 3,
 602:                      ColorSpace.TYPE_Yxy, 3, ColorSpace.TYPE_HLS, 3,
 603:                      ColorSpace.TYPE_3CLR, 3, ColorSpace.TYPE_4CLR, 4,
 604:                      ColorSpace.TYPE_5CLR, 5, ColorSpace.TYPE_6CLR, 6,
 605:                      ColorSpace.TYPE_7CLR, 7, ColorSpace.TYPE_8CLR, 8,
 606:                      ColorSpace.TYPE_9CLR, 9, ColorSpace.TYPE_ACLR, 10,
 607:                      ColorSpace.TYPE_BCLR, 11, ColorSpace.TYPE_CCLR, 12,
 608:                      ColorSpace.TYPE_DCLR, 13, ColorSpace.TYPE_ECLR, 14,
 609:                      ColorSpace.TYPE_FCLR, 15
 610:                    };
 611:     for (int i = 0; i < lookup.length; i += 2)
 612:       if (header.getColorSpace() == lookup[i])
 613:         return lookup[i + 1];
 614:     return 3; // should never happen.
 615:   }
 616: 
 617:   /**
 618:    * After deserializing we must determine if the class we want
 619:    * is really one of the more specialized ICC_ProfileRGB or
 620:    * ICC_ProfileGray classes.
 621:    */
 622:   protected Object readResolve() throws ObjectStreamException
 623:   {
 624:     if (isRGBProfile(header, tagTable))
 625:       return new ICC_ProfileRGB(getData());
 626:     if (isGrayProfile(header, tagTable))
 627:       return new ICC_ProfileGray(getData());
 628:     return this;
 629:   }
 630: 
 631:   /**
 632:    * Deserializes an instance
 633:    */
 634:   private void readObject(ObjectInputStream s)
 635:                    throws IOException, ClassNotFoundException
 636:   {
 637:     s.defaultReadObject();
 638:     String predef = (String) s.readObject();
 639:     byte[] data = (byte[]) s.readObject();
 640: 
 641:     if (data != null)
 642:       {
 643:         header = new ProfileHeader(data);
 644:         tagTable = createTagTable(data);
 645:         profileID = -1; // Not a predefined color space
 646:       }
 647: 
 648:     if (predef != null)
 649:       {
 650:         predef = predef.intern();
 651:         if (predef.equals("CS_sRGB"))
 652:           createProfile(ColorSpace.CS_sRGB);
 653:         if (predef.equals("CS_LINEAR_RGB"))
 654:           createProfile(ColorSpace.CS_LINEAR_RGB);
 655:         if (predef.equals("CS_CIEXYZ"))
 656:           createProfile(ColorSpace.CS_CIEXYZ);
 657:         if (predef.equals("CS_GRAY"))
 658:           createProfile(ColorSpace.CS_GRAY);
 659:         if (predef.equals("CS_PYCC"))
 660:           createProfile(ColorSpace.CS_PYCC);
 661:       }
 662:   }
 663: 
 664:   /**
 665:    * Serializes an instance
 666:    * The format is a String and a byte array,
 667:    * The string is non-null if the instance is one of the built-in profiles.
 668:    * Otherwise the byte array is non-null and represents the profile data.
 669:    */
 670:   private void writeObject(ObjectOutputStream s) throws IOException
 671:   {
 672:     s.defaultWriteObject();
 673:     if (profileID == ColorSpace.CS_sRGB)
 674:       s.writeObject("CS_sRGB");
 675:     else if (profileID == ColorSpace.CS_LINEAR_RGB)
 676:       s.writeObject("CS_LINEAR_RGB");
 677:     else if (profileID == ColorSpace.CS_CIEXYZ)
 678:       s.writeObject("CS_CIEXYZ");
 679:     else if (profileID == ColorSpace.CS_GRAY)
 680:       s.writeObject("CS_GRAY");
 681:     else if (profileID == ColorSpace.CS_PYCC)
 682:       s.writeObject("CS_PYCC");
 683:     else
 684:       {
 685:         s.writeObject(null); // null string
 686:         s.writeObject(getData()); // data
 687:         return;
 688:       }
 689:     s.writeObject(null); // null data
 690:   }
 691: 
 692:   /**
 693:    * Sorts a ICC profile byte array into TagEntry objects stored in
 694:    * a hash table.
 695:    */
 696:   private static Hashtable createTagTable(byte[] data)
 697:                                    throws IllegalArgumentException
 698:   {
 699:     ByteBuffer buf = ByteBuffer.wrap(data);
 700:     int nTags = buf.getInt(tagTableOffset);
 701: 
 702:     Hashtable tagTable = new Hashtable();
 703:     for (int i = 0; i < nTags; i++)
 704:       {
 705:         TagEntry te = new TagEntry(buf.getInt(tagTableOffset
 706:                                               + i * TagEntry.entrySize + 4),
 707:                                    buf.getInt(tagTableOffset
 708:                                               + i * TagEntry.entrySize + 8),
 709:                                    buf.getInt(tagTableOffset
 710:                                               + i * TagEntry.entrySize + 12),
 711:                                    data);
 712: 
 713:         if (tagTable.put(te.hashKey(), te) != null)
 714:           throw new IllegalArgumentException("Duplicate tag in profile:" + te);
 715:       }
 716:     return tagTable;
 717:   }
 718: 
 719:   /**
 720:    * Returns the total size of the padded, stored data
 721:    * Note: Tags must be stored on 4-byte aligned offsets.
 722:    */
 723:   private int getSize()
 724:   {
 725:     int totalSize = ProfileHeader.HEADERSIZE; // size of header
 726: 
 727:     int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize; // size of tag table
 728:     if ((tagTableSize & 0x0003) != 0)
 729:       tagTableSize += 4 - (tagTableSize & 0x0003); // pad
 730:     totalSize += tagTableSize;
 731: 
 732:     Enumeration e = tagTable.elements();
 733:     while (e.hasMoreElements())
 734:       { // tag data
 735:         int tagSize = ((TagEntry) e.nextElement()).getSize();
 736:         if ((tagSize & 0x0003) != 0)
 737:           tagSize += 4 - (tagSize & 0x0003); // pad
 738:         totalSize += tagSize;
 739:       }
 740:     return totalSize;
 741:   }
 742: 
 743:   /**
 744:    * Generates the tag index table
 745:    */
 746:   private byte[] getTagTable()
 747:   {
 748:     int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize;
 749:     if ((tagTableSize & 0x0003) != 0)
 750:       tagTableSize += 4 - (tagTableSize & 0x0003); // pad
 751: 
 752:     int offset = 4;
 753:     int tagOffset = ProfileHeader.HEADERSIZE + tagTableSize;
 754:     ByteBuffer buf = ByteBuffer.allocate(tagTableSize);
 755:     buf.putInt(tagTable.size()); // number of tags
 756: 
 757:     Enumeration e = tagTable.elements();
 758:     while (e.hasMoreElements())
 759:       {
 760:         TagEntry tag = (TagEntry) e.nextElement();
 761:         buf.putInt(offset, tag.getSignature());
 762:         buf.putInt(offset + 4, tagOffset);
 763:         buf.putInt(offset + 8, tag.getSize());
 764:         tag.setOffset(tagOffset);
 765:         int tagSize = tag.getSize();
 766:         if ((tagSize & 0x0003) != 0)
 767:           tagSize += 4 - (tagSize & 0x0003); // pad
 768:         tagOffset += tagSize;
 769:         offset += 12;
 770:       }
 771:     return buf.array();
 772:   }
 773: 
 774:   /**
 775:    * Returns if the criteria for an ICC_ProfileRGB are met.
 776:    * This means:
 777:    * Color space is TYPE_RGB
 778:    * (r,g,b)ColorantTags included
 779:    * (r,g,b)TRCTags included
 780:    * mediaWhitePointTag included
 781:    */
 782:   private static boolean isRGBProfile(ProfileHeader header, Hashtable tags)
 783:   {
 784:     if (header.getColorSpace() != ColorSpace.TYPE_RGB)
 785:       return false;
 786:     if (tags.get(TagEntry.tagHashKey(icSigRedColorantTag)) == null)
 787:       return false;
 788:     if (tags.get(TagEntry.tagHashKey(icSigGreenColorantTag)) == null)
 789:       return false;
 790:     if (tags.get(TagEntry.tagHashKey(icSigBlueColorantTag)) == null)
 791:       return false;
 792:     if (tags.get(TagEntry.tagHashKey(icSigRedTRCTag)) == null)
 793:       return false;
 794:     if (tags.get(TagEntry.tagHashKey(icSigGreenTRCTag)) == null)
 795:       return false;
 796:     if (tags.get(TagEntry.tagHashKey(icSigBlueTRCTag)) == null)
 797:       return false;
 798:     return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null);
 799:   }
 800: 
 801:   /**
 802:    * Returns if the criteria for an ICC_ProfileGray are met.
 803:    * This means:
 804:    * Colorspace is TYPE_GRAY
 805:    * grayTRCTag included
 806:    * mediaWhitePointTag included
 807:    */
 808:   private static boolean isGrayProfile(ProfileHeader header, Hashtable tags)
 809:   {
 810:     if (header.getColorSpace() != ColorSpace.TYPE_GRAY)
 811:       return false;
 812:     if (tags.get(TagEntry.tagHashKey(icSigGrayTRCTag)) == null)
 813:       return false;
 814:     return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null);
 815:   }
 816: 
 817:   /**
 818:    * Returns curve data for a 'curv'-type tag
 819:    * If it's a gamma curve, a single entry will be returned with the
 820:    * gamma value (including 1.0 for linear response)
 821:    * Otherwise the TRC table is returned.
 822:    *
 823:    * (Package private - used by ICC_ProfileRGB and ICC_ProfileGray)
 824:    */
 825:   short[] getCurve(int signature)
 826:   {
 827:     byte[] data = getData(signature);
 828:     short[] curve;
 829: 
 830:     // can't find tag?
 831:     if (data == null)
 832:       return null;
 833: 
 834:     // not an curve type tag?
 835:     ByteBuffer buf = ByteBuffer.wrap(data);
 836:     if (buf.getInt(0) != 0x63757276) // 'curv' type
 837:       return null;
 838:     int count = buf.getInt(8);
 839:     if (count == 0)
 840:       {
 841:         curve = new short[1];
 842:         curve[0] = 0x0100; // 1.00 in u8fixed8
 843:         return curve;
 844:       }
 845:     if (count == 1)
 846:       {
 847:         curve = new short[1];
 848:         curve[0] = buf.getShort(12); // other u8fixed8 gamma
 849:         return curve;
 850:       }
 851:     curve = new short[count];
 852:     for (int i = 0; i < count; i++)
 853:       curve[i] = buf.getShort(12 + i * 2);
 854:     return curve;
 855:   }
 856: 
 857:   /**
 858:    * Returns XYZ tristimulus values for an 'XYZ ' type tag
 859:    * @return the XYZ values, or null if the tag was not an 'XYZ ' type tag.
 860:    *
 861:    * (Package private - used by ICC_ProfileXYZ and ICC_ProfileGray)
 862:    */
 863:   float[] getXYZData(int signature)
 864:   {
 865:     byte[] data = getData(signature);
 866: 
 867:     // can't find tag?
 868:     if (data == null)
 869:       return null;
 870: 
 871:     // not an XYZData type tag?
 872:     ByteBuffer buf = ByteBuffer.wrap(data);
 873:     if (buf.getInt(0) != icSigXYZData) // 'XYZ ' type
 874:       return null;
 875: 
 876:     float[] point = new float[3];
 877: 
 878:     // get the X,Y,Z tristimulus values
 879:     point[0] = ((float) buf.getInt(8)) / 65536f;
 880:     point[1] = ((float) buf.getInt(12)) / 65536f;
 881:     point[2] = ((float) buf.getInt(16)) / 65536f;
 882:     return point;
 883:   }
 884: 
 885:   /**
 886:    * Returns the profile ID if it's a predefined profile
 887:    * Or -1 for a profile loaded from an ICC profile
 888:    *
 889:    * (Package private - used by ICC_ColorSpace)
 890:    */
 891:   int isPredefined()
 892:   {
 893:     return profileID;
 894:   }
 895: 
 896:   /**
 897:    * Creates a tag of XYZ-value type.
 898:    */
 899:   private byte[] makeXYZData(float[] values)
 900:   {
 901:     ByteBuffer buf = ByteBuffer.allocate(20);
 902:     buf.putInt(0, icSigXYZData); // 'XYZ '
 903:     buf.putInt(4, 0);
 904:     buf.putInt(8, (int) (values[0] * 65536.0));
 905:     buf.putInt(12, (int) (values[1] * 65536.0));
 906:     buf.putInt(16, (int) (values[2] * 65536.0));
 907:     return buf.array();
 908:   }
 909: 
 910:   /**
 911:    * Creates a tag of text type
 912:    */
 913:   private byte[] makeTextTag(String text)
 914:   {
 915:     int length = text.length();
 916:     ByteBuffer buf = ByteBuffer.allocate(8 + length + 1);
 917:     byte[] data;
 918:     try
 919:       {
 920:         data = text.getBytes("US-ASCII");
 921:       }
 922:     catch (UnsupportedEncodingException e)
 923:       {
 924:         data = new byte[length]; // shouldn't happen
 925:       }
 926: 
 927:     buf.putInt(0, (int) 0x74657874); // 'text'
 928:     buf.putInt(4, 0);
 929:     for (int i = 0; i < length; i++)
 930:       buf.put(8 + i, data[i]);
 931:     buf.put(8 + length, (byte) 0); // null-terminate
 932:     return buf.array();
 933:   }
 934: 
 935:   /**
 936:    * Creates a tag of textDescriptionType
 937:    */
 938:   private byte[] makeDescTag(String text)
 939:   {
 940:     int length = text.length();
 941:     ByteBuffer buf = ByteBuffer.allocate(90 + length + 1);
 942:     buf.putInt(0, (int) 0x64657363); // 'desc'
 943:     buf.putInt(4, 0); // reserved
 944:     buf.putInt(8, length + 1); // ASCII length, including null termination
 945:     byte[] data;
 946: 
 947:     try
 948:       {
 949:         data = text.getBytes("US-ASCII");
 950:       }
 951:     catch (UnsupportedEncodingException e)
 952:       {
 953:         data = new byte[length]; // shouldn't happen
 954:       }
 955: 
 956:     for (int i = 0; i < length; i++)
 957:       buf.put(12 + i, data[i]);
 958:     buf.put(12 + length, (byte) 0); // null-terminate
 959: 
 960:     for (int i = 0; i < 39; i++)
 961:       buf.putShort(13 + length + (i * 2), (short) 0); // 78 bytes we can ignore
 962: 
 963:     return buf.array();
 964:   }
 965: 
 966:   /**
 967:    * Creates a tag of TRC type (linear curve)
 968:    */
 969:   private byte[] makeTRC()
 970:   {
 971:     ByteBuffer buf = ByteBuffer.allocate(12);
 972:     buf.putInt(0, 0x63757276); // 'curv' type
 973:     buf.putInt(4, 0); // reserved
 974:     buf.putInt(8, 0);
 975:     return buf.array();
 976:   }
 977: 
 978:   /**
 979:    * Creates a tag of TRC type (single gamma value)
 980:    */
 981:   private byte[] makeTRC(float gamma)
 982:   {
 983:     short gammaValue = (short) (gamma * 256f);
 984:     ByteBuffer buf = ByteBuffer.allocate(14);
 985:     buf.putInt(0, 0x63757276); // 'curv' type
 986:     buf.putInt(4, 0); // reserved
 987:     buf.putInt(8, 1);
 988:     buf.putShort(12, gammaValue); // 1.00 in u8fixed8
 989:     return buf.array();
 990:   }
 991: 
 992:   /**
 993:    * Creates a tag of TRC type (TRC curve points)
 994:    */
 995:   private byte[] makeTRC(float[] trc)
 996:   {
 997:     ByteBuffer buf = ByteBuffer.allocate(12 + 2 * trc.length);
 998:     buf.putInt(0, 0x63757276); // 'curv' type
 999:     buf.putInt(4, 0); // reserved
1000:     buf.putInt(8, trc.length); // number of points
1001: 
1002:     // put the curve values
1003:     for (int i = 0; i < trc.length; i++)
1004:       buf.putShort(12 + i * 2, (short) (trc[i] * 65535f));
1005: 
1006:     return buf.array();
1007:   }
1008: 
1009:   /**
1010:    * Creates an identity color lookup table.
1011:    */
1012:   private byte[] makeIdentityClut()
1013:   {
1014:     final int nIn = 3;
1015:     final int nOut = 3;
1016:     final int nInEntries = 256;
1017:     final int nOutEntries = 256;
1018:     final int gridpoints = 16;
1019: 
1020:     // gridpoints**nIn
1021:     final int clutSize = 2 * nOut * gridpoints * gridpoints * gridpoints;
1022:     final int totalSize = clutSize + 2 * nInEntries * nIn
1023:                           + 2 * nOutEntries * nOut + 52;
1024: 
1025:     ByteBuffer buf = ByteBuffer.allocate(totalSize);
1026:     buf.putInt(0, 0x6D667432); // 'mft2'
1027:     buf.putInt(4, 0); // reserved
1028:     buf.put(8, (byte) nIn); // number input channels
1029:     buf.put(9, (byte) nOut); // number output channels
1030:     buf.put(10, (byte) gridpoints); // number gridpoints
1031:     buf.put(11, (byte) 0); // padding
1032: 
1033:     // identity matrix
1034:     buf.putInt(12, 65536); // = 1 in s15.16 fixed point
1035:     buf.putInt(16, 0);
1036:     buf.putInt(20, 0);
1037:     buf.putInt(24, 0);
1038:     buf.putInt(28, 65536);
1039:     buf.putInt(32, 0);
1040:     buf.putInt(36, 0);
1041:     buf.putInt(40, 0);
1042:     buf.putInt(44, 65536);
1043: 
1044:     buf.putShort(48, (short) nInEntries); // input table entries
1045:     buf.putShort(50, (short) nOutEntries); // output table entries
1046: 
1047:     // write the linear input channels, unsigned 16.16 fixed point,
1048:     // from 0.0 to FF.FF
1049:     for (int channel = 0; channel < 3; channel++)
1050:       for (int i = 0; i < nInEntries; i++)
1051:         {
1052:           short n = (short) ((i << 8) | i); // assumes 256 entries
1053:           buf.putShort(52 + (channel * nInEntries + i) * 2, n);
1054:         }
1055:     int clutOffset = 52 + nInEntries * nIn * 2;
1056: 
1057:     for (int x = 0; x < gridpoints; x++)
1058:       for (int y = 0; y < gridpoints; y++)
1059:         for (int z = 0; z < gridpoints; z++)
1060:           {
1061:             int offset = clutOffset + z * 2 * nOut + y * gridpoints * 2 * nOut
1062:                          + x * gridpoints * gridpoints * 2 * nOut;
1063:             double xf = ((double) x) / ((double) gridpoints - 1.0);
1064:             double yf = ((double) y) / ((double) gridpoints - 1.0);
1065:             double zf = ((double) z) / ((double) gridpoints - 1.0);
1066:             buf.putShort(offset, (short) (xf * 65535.0));
1067:             buf.putShort(offset + 2, (short) (yf * 65535.0));
1068:             buf.putShort(offset + 4, (short) (zf * 65535.0));
1069:           }
1070: 
1071:     for (int channel = 0; channel < 3; channel++)
1072:       for (int i = 0; i < nOutEntries; i++)
1073:         {
1074:           short n = (short) ((i << 8) | i); // assumes 256 entries
1075:           buf.putShort(clutOffset + clutSize + (channel * nOutEntries + i) * 2,
1076:                        n);
1077:         }
1078: 
1079:     return buf.array();
1080:   }
1081: 
1082:   /**
1083:    * Creates profile data corresponding to the built-in colorspaces.
1084:    */
1085:   private void createProfile(int colorSpace) throws IllegalArgumentException
1086:   {
1087:     this.profileID = colorSpace;
1088:     header = new ProfileHeader();
1089:     tagTable = new Hashtable();
1090: 
1091:     switch (colorSpace)
1092:       {
1093:       case ColorSpace.CS_sRGB:
1094:         createRGBProfile();
1095:         return;
1096:       case ColorSpace.CS_LINEAR_RGB:
1097:         createLinearRGBProfile();
1098:         return;
1099:       case ColorSpace.CS_CIEXYZ:
1100:         createCIEProfile();
1101:         return;
1102:       case ColorSpace.CS_GRAY:
1103:         createGrayProfile();
1104:         return;
1105:       case ColorSpace.CS_PYCC:
1106:         createPyccProfile();
1107:         return;
1108:       default:
1109:         throw new IllegalArgumentException("Not a predefined color space!");
1110:       }
1111:   }
1112: 
1113:   /**
1114:    * Creates an ICC_Profile representing the sRGB color space
1115:    */
1116:   private void createRGBProfile()
1117:   {
1118:     header.setColorSpace( ColorSpace.TYPE_RGB );
1119:     header.setProfileColorSpace( ColorSpace.TYPE_XYZ );
1120:     ICC_ColorSpace cs = new ICC_ColorSpace(this);
1121: 
1122:     float[] r = { 1f, 0f, 0f };
1123:     float[] g = { 0f, 1f, 0f };
1124:     float[] b = { 0f, 0f, 1f };
1125:     float[] black = { 0f, 0f, 0f };
1126: 
1127:     // CIE 1931 D50 white point (in Lab coordinates)
1128:     float[] white = D50;
1129: 
1130:     // Get tristimulus values (matrix elements)
1131:     r = cs.toCIEXYZ(r);
1132:     g = cs.toCIEXYZ(g);
1133:     b = cs.toCIEXYZ(b);
1134: 
1135:     // Generate the sRGB TRC curve, this is the linear->nonlinear
1136:     // RGB transform.
1137:     cs = new ICC_ColorSpace(getInstance(ICC_ColorSpace.CS_LINEAR_RGB));
1138:     float[] points = new float[TRC_POINTS];
1139:     float[] in = new float[3];
1140:     for (int i = 0; i < TRC_POINTS; i++)
1141:       {
1142:         in[0] = in[1] = in[2] = ((float) i) / ((float) TRC_POINTS - 1);
1143:         in = cs.fromRGB(in);
1144:         // Note this value is the same for all components.
1145:         points[i] = in[0];
1146:       }
1147: 
1148:     setData(icSigRedColorantTag, makeXYZData(r));
1149:     setData(icSigGreenColorantTag, makeXYZData(g));
1150:     setData(icSigBlueColorantTag, makeXYZData(b));
1151:     setData(icSigMediaWhitePointTag, makeXYZData(white));
1152:     setData(icSigMediaBlackPointTag, makeXYZData(black));
1153:     setData(icSigRedTRCTag, makeTRC(points));
1154:     setData(icSigGreenTRCTag, makeTRC(points));
1155:     setData(icSigBlueTRCTag, makeTRC(points));
1156:     setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1157:     setData(icSigProfileDescriptionTag, makeDescTag("Generic sRGB"));
1158:     this.profileID = ColorSpace.CS_sRGB;
1159:   }
1160: 
1161:   /**
1162:    * Creates an linear sRGB profile
1163:    */
1164:   private void createLinearRGBProfile()
1165:   {
1166:     header.setColorSpace(ColorSpace.TYPE_RGB);
1167:     header.setProfileColorSpace(ColorSpace.TYPE_XYZ);
1168:     ICC_ColorSpace cs = new ICC_ColorSpace(this);
1169: 
1170:     float[] r = { 1f, 0f, 0f };
1171:     float[] g = { 0f, 1f, 0f };
1172:     float[] b = { 0f, 0f, 1f };
1173:     float[] black = { 0f, 0f, 0f };
1174: 
1175:     float[] white = D50;
1176: 
1177:     // Get tristimulus values (matrix elements)
1178:     r = cs.toCIEXYZ(r);
1179:     g = cs.toCIEXYZ(g);
1180:     b = cs.toCIEXYZ(b);
1181: 
1182:     setData(icSigRedColorantTag, makeXYZData(r));
1183:     setData(icSigGreenColorantTag, makeXYZData(g));
1184:     setData(icSigBlueColorantTag, makeXYZData(b));
1185: 
1186:     setData(icSigMediaWhitePointTag, makeXYZData(white));
1187:     setData(icSigMediaBlackPointTag, makeXYZData(black));
1188: 
1189:     setData(icSigRedTRCTag, makeTRC());
1190:     setData(icSigGreenTRCTag, makeTRC());
1191:     setData(icSigBlueTRCTag, makeTRC());
1192:     setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1193:     setData(icSigProfileDescriptionTag, makeDescTag("Linear RGB"));
1194:     this.profileID = ColorSpace.CS_LINEAR_RGB;
1195:   }
1196: 
1197:   /**
1198:    * Creates an CIE XYZ identity profile
1199:    */
1200:   private void createCIEProfile()
1201:   {
1202:     header.setColorSpace( ColorSpace.TYPE_XYZ );
1203:     header.setProfileColorSpace( ColorSpace.TYPE_XYZ );
1204:     header.setProfileClass( CLASS_COLORSPACECONVERSION );
1205:     ICC_ColorSpace cs = new ICC_ColorSpace(this);
1206: 
1207:     float[] white = D50;
1208: 
1209:     setData(icSigMediaWhitePointTag, makeXYZData(white));
1210:     setData(icSigAToB0Tag, makeIdentityClut());
1211:     setData(icSigBToA0Tag, makeIdentityClut());
1212:     setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1213:     setData(icSigProfileDescriptionTag, makeDescTag("CIE XYZ identity profile"));
1214:     this.profileID = ColorSpace.CS_CIEXYZ;
1215:   }
1216: 
1217:   /**
1218:    * Creates a linear gray ICC_Profile
1219:    */
1220:   private void createGrayProfile()
1221:   {
1222:     header.setColorSpace(ColorSpace.TYPE_GRAY);
1223:     header.setProfileColorSpace(ColorSpace.TYPE_XYZ);
1224: 
1225:     // CIE 1931 D50 white point (in Lab coordinates)
1226:     float[] white = D50;
1227: 
1228:     setData(icSigMediaWhitePointTag, makeXYZData(white));
1229:     setData(icSigGrayTRCTag, makeTRC(1.0f));
1230:     setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1231:     setData(icSigProfileDescriptionTag, makeDescTag("Linear grayscale"));
1232:     this.profileID = ColorSpace.CS_GRAY;
1233:   }
1234: 
1235:   /**
1236:    * XXX Implement me
1237:    */
1238:   private void createPyccProfile()
1239:   {
1240:     header.setColorSpace(ColorSpace.TYPE_3CLR);
1241:     header.setProfileColorSpace(ColorSpace.TYPE_XYZ);
1242: 
1243:     // Create CLUTs here. :-)
1244: 
1245:     setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1246:     setData(icSigProfileDescriptionTag, makeDescTag("Photo YCC"));
1247:     this.profileID = ColorSpace.CS_PYCC;
1248:   }
1249: } // class ICC_Profile