Merge from the pain train
[official-gcc.git] / libjava / java / awt / color / ICC_Profile.java
blob791ea9f6cf1b02ef20b67f3186c7ccc9cf0b2452
1 /* ICC_Profile.java -- color space profiling
2 Copyright (C) 2000, 2002, 2004 Free Software Foundation
4 This file is part of GNU Classpath.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
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.
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. */
39 package java.awt.color;
41 import gnu.java.awt.color.ProfileHeader;
42 import gnu.java.awt.color.TagEntry;
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;
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)
66 * ICC profiles calibrated to specific input/output devices are used when color
67 * fidelity is of importance.
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.
73 * An ICC_ColorSpace object can then be created to transform color values
74 * through the profile.
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)
81 * @author Sven de Marothy
82 * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
83 * @since 1.2
85 public class ICC_Profile implements Serializable
87 /**
88 * Compatible with JDK 1.2+.
90 private static final long serialVersionUID = -3938515861990936766L;
92 /**
93 * ICC Profile classes
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;
104 * ICC Profile class signatures
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'
115 * Color space signatures
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'
144 * Rendering intents
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;
152 * Tag signatures
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'
201 * Non-ICC tag 'head' for use in retrieving the header with getData()
203 public static final int icSigHead = 0x68656164;
206 * Header offsets
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;
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;
235 * offset of the Tag table
237 private static final int tagTableOffset = 128;
240 * @serial
242 private static final int iccProfileSerializedDataVersion = 1;
245 * Constants related to generating profiles for
246 * built-in colorspace profiles
249 * Copyright notice to stick into built-in-profile files.
251 private static final String copyrightNotice = "Generated by GNU Classpath.";
254 * Resolution of the TRC to use for predefined profiles.
255 * 1024 should suffice.
257 private static final int TRC_POINTS = 1024;
260 * CIE 1931 D50 white point (in Lab coordinates)
262 private static final float[] D50 = { 0.96422f, 1.00f, 0.82521f };
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)
270 private transient int profileID;
273 * The profile header data
275 private transient ProfileHeader header;
278 * A hashtable containing the profile tags as TagEntry objects
280 private transient Hashtable tagTable;
283 * Contructor for predefined colorspaces
285 ICC_Profile(int profileID)
287 header = null;
288 tagTable = null;
289 createProfile(profileID);
293 * Constructs an ICC_Profile from a header and a table of loaded tags.
295 ICC_Profile(ProfileHeader h, Hashtable tags) throws IllegalArgumentException
297 header = h;
298 tagTable = tags;
299 profileID = -1; // Not a predefined color space
303 * Constructs an ICC_Profile from a byte array of data.
305 ICC_Profile(byte[] data) throws IllegalArgumentException
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
315 * Free up the used memory.
317 protected void finalize()
322 * Returns an ICC_Profile instance from a byte array of profile data.
324 * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
325 * may be returned if appropriate.
327 * @throws IllegalArgumentException if the profile data is an invalid
328 * v2 profile.
330 * @param data - the profile data
331 * @return An ICC_Profile object
333 public static ICC_Profile getInstance(byte[] data)
335 ProfileHeader header = new ProfileHeader(data);
337 // verify it as a correct ICC header, including size
338 header.verifyHeader(data.length);
340 Hashtable tags = createTagTable(data);
342 if (isRGBProfile(header, tags))
343 return new ICC_ProfileRGB(data);
344 if (isGrayProfile(header, tags))
345 return new ICC_ProfileGray(data);
347 return new ICC_Profile(header, tags);
351 * Returns an predefined ICC_Profile instance.
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)
356 * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
357 * may be returned if appropriate.
359 * @return An ICC_Profile object
361 public static ICC_Profile getInstance(int cspace)
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);
371 * Returns an ICC_Profile instance from an ICC Profile file.
373 * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
374 * may be returned if appropriate.
376 * @throws IllegalArgumentException if the profile data is an invalid
377 * v2 profile.
378 * @throws IOException if the file could not be read.
380 * @param filename - the file name of the profile file.
381 * @return An ICC_Profile object
383 public static ICC_Profile getInstance(String filename)
384 throws IOException
386 return getInstance(new FileInputStream(filename));
390 * Returns an ICC_Profile instance from an InputStream.
392 * This method can be used for reading ICC profiles embedded in files
393 * which support this. (JPEG and SVG for instance).
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.
400 * An instance of the specialized classes ICC_ProfileRGB or ICC_ProfileGray
401 * may be returned if appropriate.
403 * @throws IllegalArgumentException if the profile data is an invalid
404 * v2 profile.
405 * @throws IOException if the stream could not be read.
407 * @param in - the input stream to read the profile from.
408 * @return An ICC_Profile object
410 public static ICC_Profile getInstance(InputStream in)
411 throws IOException
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");
418 ProfileHeader header = new ProfileHeader(headerData);
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);
424 // get the size
425 byte[] data = new byte[header.getSize()];
426 System.arraycopy(headerData, 0, data, 0, ProfileHeader.HEADERSIZE);
428 // read the rest
429 if (in.read(data, ProfileHeader.HEADERSIZE,
430 header.getSize() - ProfileHeader.HEADERSIZE) != header.getSize()
431 - ProfileHeader.HEADERSIZE)
432 throw new IOException("Incorrect profile size");
434 return getInstance(data);
438 * Returns the major version number
440 public int getMajorVersion()
442 return header.getMajorVersion();
446 * Returns the minor version number.
448 * Only the least-significant byte contains data, in BCD form:
449 * the least-significant nibble is the BCD bug fix revision,
450 * the most-significant nibble is the BCD minor revision number.
452 * (E.g. For a v2.1.0 profile this will return <code>0x10</code>)
454 public int getMinorVersion()
456 return header.getMinorVersion();
460 * Returns the device class of this profile,
462 * (E.g. CLASS_INPUT for a scanner profile,
463 * CLASS_OUTPUT for a printer)
465 public int getProfileClass()
467 return header.getProfileClass();
471 * Returns the color space of this profile, in terms
472 * of the color space constants defined in ColorSpace.
473 * (For example, it may be a ColorSpace.TYPE_RGB)
475 public int getColorSpaceType()
477 return header.getColorSpace();
481 * Returns the color space of this profile's Profile Connection Space (OCS)
483 * In terms of the color space constants defined in ColorSpace.
484 * This may be TYPE_XYZ or TYPE_Lab
486 public int getPCSType()
488 return header.getProfileColorSpace();
492 * Writes the profile data to an ICC profile file.
493 * @param filename - The name of the file to write
494 * @throws IOException if the write failed.
496 public void write(String filename) throws IOException
498 FileOutputStream out = new FileOutputStream(filename);
499 write(out);
500 out.flush();
501 out.close();
505 * Writes the profile data in ICC profile file-format to a stream.
506 * This is useful for embedding ICC profiles in file formats which
507 * support this (such as JPEG and SVG).
509 * The stream is not closed after writing.
510 * @param out - The outputstream to which the profile data should be written
511 * @throws IOException if the write failed.
513 public void write(OutputStream out) throws IOException
515 out.write(getData());
519 * Returns the data corresponding to this ICC_Profile as a byte array.
521 * @return The data in a byte array,
522 * where the first element corresponds to first byte of the profile file.
524 public byte[] getData()
526 int size = getSize();
527 byte[] data = new byte[size];
529 // Header
530 System.arraycopy(header.getData(size), 0, data, 0, ProfileHeader.HEADERSIZE);
531 // # of tags
532 byte[] tt = getTagTable();
533 System.arraycopy(tt, 0, data, ProfileHeader.HEADERSIZE, tt.length);
535 Enumeration e = tagTable.elements();
536 while (e.hasMoreElements())
538 TagEntry tag = (TagEntry) e.nextElement();
539 System.arraycopy(tag.getData(), 0,
540 data, tag.getOffset(), tag.getSize());
542 return data;
546 * Returns the ICC profile tag data
547 * The non ICC-tag icSigHead is also permitted to request the header data.
549 * @param tagSignature The ICC signature of the requested tag
550 * @return A byte array containing the tag data
552 public byte[] getData(int tagSignature)
554 if (tagSignature == icSigHead)
555 return header.getData(getSize());
557 TagEntry t = (TagEntry) tagTable.get(TagEntry.tagHashKey(tagSignature));
558 if (t == null)
559 return null;
560 return t.getData();
564 * Sets the ICC profile tag data.
566 * Note that an ICC profile can only contain one tag of each type, if
567 * a tag already exists with the given signature, it is replaced.
569 * @param tagSignature - The signature of the tag to set
570 * @param data - A byte array containing the tag data
572 public void setData(int tagSignature, byte[] data)
574 profileID = -1; // Not a predefined color space if modified.
576 if (tagSignature == icSigHead)
577 header = new ProfileHeader(data);
578 else
580 TagEntry t = new TagEntry(tagSignature, data);
581 tagTable.put(t.hashKey(), t);
586 * Get the number of components in the profile's device color space.
588 public int getNumComponents()
590 int[] lookup =
592 ColorSpace.TYPE_RGB, 3, ColorSpace.TYPE_CMY, 3,
593 ColorSpace.TYPE_CMYK, 4, ColorSpace.TYPE_GRAY, 1,
594 ColorSpace.TYPE_YCbCr, 3, ColorSpace.TYPE_XYZ, 3,
595 ColorSpace.TYPE_Lab, 3, ColorSpace.TYPE_HSV, 3,
596 ColorSpace.TYPE_2CLR, 2, ColorSpace.TYPE_Luv, 3,
597 ColorSpace.TYPE_Yxy, 3, ColorSpace.TYPE_HLS, 3,
598 ColorSpace.TYPE_3CLR, 3, ColorSpace.TYPE_4CLR, 4,
599 ColorSpace.TYPE_5CLR, 5, ColorSpace.TYPE_6CLR, 6,
600 ColorSpace.TYPE_7CLR, 7, ColorSpace.TYPE_8CLR, 8,
601 ColorSpace.TYPE_9CLR, 9, ColorSpace.TYPE_ACLR, 10,
602 ColorSpace.TYPE_BCLR, 11, ColorSpace.TYPE_CCLR, 12,
603 ColorSpace.TYPE_DCLR, 13, ColorSpace.TYPE_ECLR, 14,
604 ColorSpace.TYPE_FCLR, 15
606 for (int i = 0; i < lookup.length; i += 2)
607 if (header.getColorSpace() == lookup[i])
608 return lookup[i + 1];
609 return 3; // should never happen.
613 * After deserializing we must determine if the class we want
614 * is really one of the more specialized ICC_ProfileRGB or
615 * ICC_ProfileGray classes.
617 protected Object readResolve() throws ObjectStreamException
619 if (isRGBProfile(header, tagTable))
620 return new ICC_ProfileRGB(getData());
621 if (isGrayProfile(header, tagTable))
622 return new ICC_ProfileGray(getData());
623 return this;
627 * Deserializes an instance
629 private void readObject(ObjectInputStream s)
630 throws IOException, ClassNotFoundException
632 s.defaultReadObject();
633 String predef = (String) s.readObject();
634 byte[] data = (byte[]) s.readObject();
636 if (data != null)
638 header = new ProfileHeader(data);
639 tagTable = createTagTable(data);
640 profileID = -1; // Not a predefined color space
643 if (predef != null)
645 predef = predef.intern();
646 if (predef.equals("CS_sRGB"))
647 createProfile(ColorSpace.CS_sRGB);
648 if (predef.equals("CS_LINEAR_RGB"))
649 createProfile(ColorSpace.CS_LINEAR_RGB);
650 if (predef.equals("CS_CIEXYZ"))
651 createProfile(ColorSpace.CS_CIEXYZ);
652 if (predef.equals("CS_GRAY"))
653 createProfile(ColorSpace.CS_GRAY);
654 if (predef.equals("CS_PYCC"))
655 createProfile(ColorSpace.CS_PYCC);
660 * Serializes an instance
661 * The format is a String and a byte array,
662 * The string is non-null if the instance is one of the built-in profiles.
663 * Otherwise the byte array is non-null and represents the profile data.
665 private void writeObject(ObjectOutputStream s) throws IOException
667 s.defaultWriteObject();
668 if (profileID == ColorSpace.CS_sRGB)
669 s.writeObject("CS_sRGB");
670 else if (profileID == ColorSpace.CS_LINEAR_RGB)
671 s.writeObject("CS_LINEAR_RGB");
672 else if (profileID == ColorSpace.CS_CIEXYZ)
673 s.writeObject("CS_CIEXYZ");
674 else if (profileID == ColorSpace.CS_GRAY)
675 s.writeObject("CS_GRAY");
676 else if (profileID == ColorSpace.CS_PYCC)
677 s.writeObject("CS_PYCC");
678 else
680 s.writeObject(null); // null string
681 s.writeObject(getData()); // data
682 return;
684 s.writeObject(null); // null data
688 * Sorts a ICC profile byte array into TagEntry objects stored in
689 * a hash table.
691 private static Hashtable createTagTable(byte[] data)
692 throws IllegalArgumentException
694 ByteBuffer buf = ByteBuffer.wrap(data);
695 int nTags = buf.getInt(tagTableOffset);
697 Hashtable tagTable = new Hashtable();
698 for (int i = 0; i < nTags; i++)
700 TagEntry te = new TagEntry(buf.getInt(tagTableOffset
701 + i * TagEntry.entrySize + 4),
702 buf.getInt(tagTableOffset
703 + i * TagEntry.entrySize + 8),
704 buf.getInt(tagTableOffset
705 + i * TagEntry.entrySize + 12),
706 data);
708 if (tagTable.put(te.hashKey(), te) != null)
709 throw new IllegalArgumentException("Duplicate tag in profile:" + te);
711 return tagTable;
715 * Returns the total size of the padded, stored data
716 * Note: Tags must be stored on 4-byte aligned offsets.
718 private int getSize()
720 int totalSize = ProfileHeader.HEADERSIZE; // size of header
722 int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize; // size of tag table
723 if ((tagTableSize & 0x0003) != 0)
724 tagTableSize += 4 - (tagTableSize & 0x0003); // pad
725 totalSize += tagTableSize;
727 Enumeration e = tagTable.elements();
728 while (e.hasMoreElements())
729 { // tag data
730 int tagSize = ((TagEntry) e.nextElement()).getSize();
731 if ((tagSize & 0x0003) != 0)
732 tagSize += 4 - (tagSize & 0x0003); // pad
733 totalSize += tagSize;
735 return totalSize;
739 * Generates the tag index table
741 private byte[] getTagTable()
743 int tagTableSize = 4 + tagTable.size() * TagEntry.entrySize;
744 if ((tagTableSize & 0x0003) != 0)
745 tagTableSize += 4 - (tagTableSize & 0x0003); // pad
747 int offset = 4;
748 int tagOffset = ProfileHeader.HEADERSIZE + tagTableSize;
749 ByteBuffer buf = ByteBuffer.allocate(tagTableSize);
750 buf.putInt(tagTable.size()); // number of tags
752 Enumeration e = tagTable.elements();
753 while (e.hasMoreElements())
755 TagEntry tag = (TagEntry) e.nextElement();
756 buf.putInt(offset, tag.getSignature());
757 buf.putInt(offset + 4, tagOffset);
758 buf.putInt(offset + 8, tag.getSize());
759 tag.setOffset(tagOffset);
760 int tagSize = tag.getSize();
761 if ((tagSize & 0x0003) != 0)
762 tagSize += 4 - (tagSize & 0x0003); // pad
763 tagOffset += tagSize;
764 offset += 12;
766 return buf.array();
770 * Returns if the criteria for an ICC_ProfileRGB are met.
771 * This means:
772 * Color space is TYPE_RGB
773 * (r,g,b)ColorantTags included
774 * (r,g,b)TRCTags included
775 * mediaWhitePointTag included
777 private static boolean isRGBProfile(ProfileHeader header, Hashtable tags)
779 if (header.getColorSpace() != ColorSpace.TYPE_RGB)
780 return false;
781 if (tags.get(TagEntry.tagHashKey(icSigRedColorantTag)) == null)
782 return false;
783 if (tags.get(TagEntry.tagHashKey(icSigGreenColorantTag)) == null)
784 return false;
785 if (tags.get(TagEntry.tagHashKey(icSigBlueColorantTag)) == null)
786 return false;
787 if (tags.get(TagEntry.tagHashKey(icSigRedTRCTag)) == null)
788 return false;
789 if (tags.get(TagEntry.tagHashKey(icSigGreenTRCTag)) == null)
790 return false;
791 if (tags.get(TagEntry.tagHashKey(icSigBlueTRCTag)) == null)
792 return false;
793 return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null);
797 * Returns if the criteria for an ICC_ProfileGray are met.
798 * This means:
799 * Colorspace is TYPE_GRAY
800 * grayTRCTag included
801 * mediaWhitePointTag included
803 private static boolean isGrayProfile(ProfileHeader header, Hashtable tags)
805 if (header.getColorSpace() != ColorSpace.TYPE_GRAY)
806 return false;
807 if (tags.get(TagEntry.tagHashKey(icSigGrayTRCTag)) == null)
808 return false;
809 return (tags.get(TagEntry.tagHashKey(icSigMediaWhitePointTag)) != null);
813 * Returns curve data for a 'curv'-type tag
814 * If it's a gamma curve, a single entry will be returned with the
815 * gamma value (including 1.0 for linear response)
816 * Otherwise the TRC table is returned.
818 * (Package private - used by ICC_ProfileRGB and ICC_ProfileGray)
820 short[] getCurve(int signature)
822 byte[] data = getData(signature);
823 short[] curve;
825 // can't find tag?
826 if (data == null)
827 return null;
829 // not an curve type tag?
830 ByteBuffer buf = ByteBuffer.wrap(data);
831 if (buf.getInt(0) != 0x63757276) // 'curv' type
832 return null;
833 int count = buf.getInt(8);
834 if (count == 0)
836 curve = new short[1];
837 curve[0] = 0x0100; // 1.00 in u8fixed8
838 return curve;
840 if (count == 1)
842 curve = new short[1];
843 curve[0] = buf.getShort(12); // other u8fixed8 gamma
844 return curve;
846 curve = new short[count];
847 for (int i = 0; i < count; i++)
848 curve[i] = buf.getShort(12 + i * 2);
849 return curve;
853 * Returns XYZ tristimulus values for an 'XYZ ' type tag
854 * @return the XYZ values, or null if the tag was not an 'XYZ ' type tag.
856 * (Package private - used by ICC_ProfileXYZ and ICC_ProfileGray)
858 float[] getXYZData(int signature)
860 byte[] data = getData(signature);
862 // can't find tag?
863 if (data == null)
864 return null;
866 // not an XYZData type tag?
867 ByteBuffer buf = ByteBuffer.wrap(data);
868 if (buf.getInt(0) != icSigXYZData) // 'XYZ ' type
869 return null;
871 float[] point = new float[3];
873 // get the X,Y,Z tristimulus values
874 point[0] = ((float) buf.getInt(8)) / 65536f;
875 point[1] = ((float) buf.getInt(12)) / 65536f;
876 point[2] = ((float) buf.getInt(16)) / 65536f;
877 return point;
881 * Returns the profile ID if it's a predefined profile
882 * Or -1 for a profile loaded from an ICC profile
884 * (Package private - used by ICC_ColorSpace)
886 int isPredefined()
888 return profileID;
892 * Creates a tag of XYZ-value type.
894 private byte[] makeXYZData(float[] values)
896 ByteBuffer buf = ByteBuffer.allocate(20);
897 buf.putInt(0, icSigXYZData); // 'XYZ '
898 buf.putInt(4, 0);
899 buf.putInt(8, (int) (values[0] * 65536.0));
900 buf.putInt(12, (int) (values[1] * 65536.0));
901 buf.putInt(16, (int) (values[2] * 65536.0));
902 return buf.array();
906 * Creates a tag of text type
908 private byte[] makeTextTag(String text)
910 int length = text.length();
911 ByteBuffer buf = ByteBuffer.allocate(8 + length + 1);
912 byte[] data;
915 data = text.getBytes("US-ASCII");
917 catch (UnsupportedEncodingException e)
919 data = new byte[length]; // shouldn't happen
922 buf.putInt(0, (int) 0x74657874); // 'text'
923 buf.putInt(4, 0);
924 for (int i = 0; i < length; i++)
925 buf.put(8 + i, data[i]);
926 buf.put(8 + length, (byte) 0); // null-terminate
927 return buf.array();
931 * Creates a tag of textDescriptionType
933 private byte[] makeDescTag(String text)
935 int length = text.length();
936 ByteBuffer buf = ByteBuffer.allocate(90 + length + 1);
937 buf.putInt(0, (int) 0x64657363); // 'desc'
938 buf.putInt(4, 0); // reserved
939 buf.putInt(8, length + 1); // ASCII length, including null termination
940 byte[] data;
944 data = text.getBytes("US-ASCII");
946 catch (UnsupportedEncodingException e)
948 data = new byte[length]; // shouldn't happen
951 for (int i = 0; i < length; i++)
952 buf.put(12 + i, data[i]);
953 buf.put(12 + length, (byte) 0); // null-terminate
955 for (int i = 0; i < 39; i++)
956 buf.putShort(13 + length + (i * 2), (short) 0); // 78 bytes we can ignore
958 return buf.array();
962 * Creates a tag of TRC type (linear curve)
964 private byte[] makeTRC()
966 ByteBuffer buf = ByteBuffer.allocate(12);
967 buf.putInt(0, 0x63757276); // 'curv' type
968 buf.putInt(4, 0); // reserved
969 buf.putInt(8, 0);
970 return buf.array();
974 * Creates a tag of TRC type (single gamma value)
976 private byte[] makeTRC(float gamma)
978 short gammaValue = (short) (gamma * 256f);
979 ByteBuffer buf = ByteBuffer.allocate(14);
980 buf.putInt(0, 0x63757276); // 'curv' type
981 buf.putInt(4, 0); // reserved
982 buf.putInt(8, 1);
983 buf.putShort(12, gammaValue); // 1.00 in u8fixed8
984 return buf.array();
988 * Creates a tag of TRC type (TRC curve points)
990 private byte[] makeTRC(float[] trc)
992 ByteBuffer buf = ByteBuffer.allocate(12 + 2 * trc.length);
993 buf.putInt(0, 0x63757276); // 'curv' type
994 buf.putInt(4, 0); // reserved
995 buf.putInt(8, trc.length); // number of points
997 // put the curve values
998 for (int i = 0; i < trc.length; i++)
999 buf.putShort(12 + i * 2, (short) (trc[i] * 65535f));
1001 return buf.array();
1005 * Creates an identity color lookup table.
1007 private byte[] makeIdentityClut()
1009 final int nIn = 3;
1010 final int nOut = 3;
1011 final int nInEntries = 256;
1012 final int nOutEntries = 256;
1013 final int gridpoints = 16;
1015 // gridpoints**nIn
1016 final int clutSize = 2 * nOut * gridpoints * gridpoints * gridpoints;
1017 final int totalSize = clutSize + 2 * nInEntries * nIn
1018 + 2 * nOutEntries * nOut + 52;
1020 ByteBuffer buf = ByteBuffer.allocate(totalSize);
1021 buf.putInt(0, 0x6D667432); // 'mft2'
1022 buf.putInt(4, 0); // reserved
1023 buf.put(8, (byte) nIn); // number input channels
1024 buf.put(9, (byte) nOut); // number output channels
1025 buf.put(10, (byte) gridpoints); // number gridpoints
1026 buf.put(11, (byte) 0); // padding
1028 // identity matrix
1029 buf.putInt(12, 65536); // = 1 in s15.16 fixed point
1030 buf.putInt(16, 0);
1031 buf.putInt(20, 0);
1032 buf.putInt(24, 0);
1033 buf.putInt(28, 65536);
1034 buf.putInt(32, 0);
1035 buf.putInt(36, 0);
1036 buf.putInt(40, 0);
1037 buf.putInt(44, 65536);
1039 buf.putShort(48, (short) nInEntries); // input table entries
1040 buf.putShort(50, (short) nOutEntries); // output table entries
1042 // write the linear input channels, unsigned 16.16 fixed point,
1043 // from 0.0 to FF.FF
1044 for (int channel = 0; channel < 3; channel++)
1045 for (int i = 0; i < nInEntries; i++)
1047 short n = (short) ((i << 8) | i); // assumes 256 entries
1048 buf.putShort(52 + (channel * nInEntries + i) * 2, n);
1050 int clutOffset = 52 + nInEntries * nIn * 2;
1052 for (int x = 0; x < gridpoints; x++)
1053 for (int y = 0; y < gridpoints; y++)
1054 for (int z = 0; z < gridpoints; z++)
1056 int offset = clutOffset + z * 2 * nOut + y * gridpoints * 2 * nOut
1057 + x * gridpoints * gridpoints * 2 * nOut;
1058 double xf = ((double) x) / ((double) gridpoints - 1.0);
1059 double yf = ((double) y) / ((double) gridpoints - 1.0);
1060 double zf = ((double) z) / ((double) gridpoints - 1.0);
1061 buf.putShort(offset, (short) (xf * 65535.0));
1062 buf.putShort(offset + 2, (short) (yf * 65535.0));
1063 buf.putShort(offset + 4, (short) (zf * 65535.0));
1066 for (int channel = 0; channel < 3; channel++)
1067 for (int i = 0; i < nOutEntries; i++)
1069 short n = (short) ((i << 8) | i); // assumes 256 entries
1070 buf.putShort(clutOffset + clutSize + (channel * nOutEntries + i) * 2,
1074 return buf.array();
1078 * Creates profile data corresponding to the built-in colorspaces.
1080 private void createProfile(int colorSpace) throws IllegalArgumentException
1082 this.profileID = colorSpace;
1083 header = new ProfileHeader();
1084 tagTable = new Hashtable();
1086 switch (colorSpace)
1088 case ColorSpace.CS_sRGB:
1089 createRGBProfile();
1090 return;
1091 case ColorSpace.CS_LINEAR_RGB:
1092 createLinearRGBProfile();
1093 return;
1094 case ColorSpace.CS_CIEXYZ:
1095 createCIEProfile();
1096 return;
1097 case ColorSpace.CS_GRAY:
1098 createGrayProfile();
1099 return;
1100 case ColorSpace.CS_PYCC:
1101 createPyccProfile();
1102 return;
1103 default:
1104 throw new IllegalArgumentException("Not a predefined color space!");
1109 * Creates an ICC_Profile representing the sRGB color space
1111 private void createRGBProfile()
1113 header.setColorSpace( ColorSpace.TYPE_RGB );
1114 header.setProfileColorSpace( ColorSpace.TYPE_XYZ );
1115 ICC_ColorSpace cs = new ICC_ColorSpace(this);
1117 float[] r = { 1f, 0f, 0f };
1118 float[] g = { 0f, 1f, 0f };
1119 float[] b = { 0f, 0f, 1f };
1120 float[] black = { 0f, 0f, 0f };
1122 // CIE 1931 D50 white point (in Lab coordinates)
1123 float[] white = D50;
1125 // Get tristimulus values (matrix elements)
1126 r = cs.toCIEXYZ(r);
1127 g = cs.toCIEXYZ(g);
1128 b = cs.toCIEXYZ(b);
1130 // Generate the sRGB TRC curve, this is the linear->nonlinear
1131 // RGB transform.
1132 cs = new ICC_ColorSpace(getInstance(ICC_ColorSpace.CS_LINEAR_RGB));
1133 float[] points = new float[TRC_POINTS];
1134 float[] in = new float[3];
1135 for (int i = 0; i < TRC_POINTS; i++)
1137 in[0] = in[1] = in[2] = ((float) i) / ((float) TRC_POINTS - 1);
1138 in = cs.fromRGB(in);
1139 // Note this value is the same for all components.
1140 points[i] = in[0];
1143 setData(icSigRedColorantTag, makeXYZData(r));
1144 setData(icSigGreenColorantTag, makeXYZData(g));
1145 setData(icSigBlueColorantTag, makeXYZData(b));
1146 setData(icSigMediaWhitePointTag, makeXYZData(white));
1147 setData(icSigMediaBlackPointTag, makeXYZData(black));
1148 setData(icSigRedTRCTag, makeTRC(points));
1149 setData(icSigGreenTRCTag, makeTRC(points));
1150 setData(icSigBlueTRCTag, makeTRC(points));
1151 setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1152 setData(icSigProfileDescriptionTag, makeDescTag("Generic sRGB"));
1153 this.profileID = ColorSpace.CS_sRGB;
1157 * Creates an linear sRGB profile
1159 private void createLinearRGBProfile()
1161 header.setColorSpace(ColorSpace.TYPE_RGB);
1162 header.setProfileColorSpace(ColorSpace.TYPE_XYZ);
1163 ICC_ColorSpace cs = new ICC_ColorSpace(this);
1165 float[] r = { 1f, 0f, 0f };
1166 float[] g = { 0f, 1f, 0f };
1167 float[] b = { 0f, 0f, 1f };
1168 float[] black = { 0f, 0f, 0f };
1170 float[] white = D50;
1172 // Get tristimulus values (matrix elements)
1173 r = cs.toCIEXYZ(r);
1174 g = cs.toCIEXYZ(g);
1175 b = cs.toCIEXYZ(b);
1177 setData(icSigRedColorantTag, makeXYZData(r));
1178 setData(icSigGreenColorantTag, makeXYZData(g));
1179 setData(icSigBlueColorantTag, makeXYZData(b));
1181 setData(icSigMediaWhitePointTag, makeXYZData(white));
1182 setData(icSigMediaBlackPointTag, makeXYZData(black));
1184 setData(icSigRedTRCTag, makeTRC());
1185 setData(icSigGreenTRCTag, makeTRC());
1186 setData(icSigBlueTRCTag, makeTRC());
1187 setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1188 setData(icSigProfileDescriptionTag, makeDescTag("Linear RGB"));
1189 this.profileID = ColorSpace.CS_LINEAR_RGB;
1193 * Creates an CIE XYZ identity profile
1195 private void createCIEProfile()
1197 header.setColorSpace( ColorSpace.TYPE_XYZ );
1198 header.setProfileColorSpace( ColorSpace.TYPE_XYZ );
1199 header.setProfileClass( CLASS_COLORSPACECONVERSION );
1200 ICC_ColorSpace cs = new ICC_ColorSpace(this);
1202 float[] white = D50;
1204 setData(icSigMediaWhitePointTag, makeXYZData(white));
1205 setData(icSigAToB0Tag, makeIdentityClut());
1206 setData(icSigBToA0Tag, makeIdentityClut());
1207 setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1208 setData(icSigProfileDescriptionTag, makeDescTag("CIE XYZ identity profile"));
1209 this.profileID = ColorSpace.CS_CIEXYZ;
1213 * Creates a linear gray ICC_Profile
1215 private void createGrayProfile()
1217 header.setColorSpace(ColorSpace.TYPE_GRAY);
1218 header.setProfileColorSpace(ColorSpace.TYPE_XYZ);
1220 // CIE 1931 D50 white point (in Lab coordinates)
1221 float[] white = D50;
1223 setData(icSigMediaWhitePointTag, makeXYZData(white));
1224 setData(icSigGrayTRCTag, makeTRC(1.0f));
1225 setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1226 setData(icSigProfileDescriptionTag, makeDescTag("Linear grayscale"));
1227 this.profileID = ColorSpace.CS_GRAY;
1231 * XXX Implement me
1233 private void createPyccProfile()
1235 header.setColorSpace(ColorSpace.TYPE_3CLR);
1236 header.setProfileColorSpace(ColorSpace.TYPE_XYZ);
1238 // Create CLUTs here. :-)
1240 setData(icSigCopyrightTag, makeTextTag(copyrightNotice));
1241 setData(icSigProfileDescriptionTag, makeDescTag("Photo YCC"));
1242 this.profileID = ColorSpace.CS_PYCC;
1244 } // class ICC_Profile