001: package org.vfny.geoserver.config;
002:
003: import java.awt.Color;
004: import java.awt.image.IndexColorModel;
005: import java.io.BufferedReader;
006: import java.io.File;
007: import java.io.FileNotFoundException;
008: import java.io.FileReader;
009: import java.io.IOException;
010: import java.util.StringTokenizer;
011: import java.util.logging.Level;
012: import java.util.logging.Logger;
013:
014: /**
015: * Loads a JASC Pal files into an {@link IndexColorModel}.
016: *
017: * <p>
018: * I made a real minor extension to the usual form of a JASC pal file which
019: * allows us to provide values in the #ffffff or 0Xffffff hex form.
020: *
021: * <p>
022: * Note that this kind of file does not support explicitly setting transparent
023: * pixel. However I have implemented this workaround, if you use less than 256
024: * colors in your paletteInverter I will accordingly set the transparent pixel to the
025: * first available position in the paletteInverter, which is palette_size. If you use
026: * 256 colors no transparency will be used for the image we generate.
027: *
028: * <p>
029: * <strong>Be aware</strong> that IrfanView does not always report correctly
030: * the size of the palette it exports. Be ready to manually correct the number
031: * of colors reported.
032: *
033: * <p>
034: * Here is an explanation of what a JASC pal file should look like:
035: *
036: * <a href="http://www.cryer.co.uk/filetypes/p/pal.htm">JASC PAL file</a>
037: *
038: * and here is a list of other possible formats we could parse (in the future if
039: * we have time or someone pays for it :-) )
040: *
041: * <a href="http://www.pl32.com/forum/viewtopic.php?t=873">alternative PAL file formats</a>
042: *
043: * @author Simone Giannecchini
044: *
045: */
046: public class PALFileLoader {
047: protected static final Logger LOGGER = org.geotools.util.logging.Logging
048: .getLogger("it.geosolutions.inversecolormap.PALFileLoader");
049:
050: /** Size of the color map we'll use. */
051: protected int mapsize;
052:
053: /** Final index color model. */
054: protected IndexColorModel indexColorModel;
055:
056: /**
057: * {@link PALFileLoader} constructor that accept a file path as a string.
058: *
059: * <p>
060: * Note that the transparentIndex pixel should not exceed the last
061: * zero-based index available for the colormap we area going to create. If
062: * this happens we might get very bad behaviour. Note also that if we set
063: * this parameter to -1 we'll get an opaque {@link IndexColorModel}.
064: *
065: * @param filePath
066: * to the aplette file.
067: * @param transparentIndex
068: * transparent pixel index (zero-based).
069: */
070: public PALFileLoader(final String filePath) {
071: this (new File(filePath));
072: }
073:
074: /**
075: * {@link PALFileLoader} constructor that accept a file.
076: *
077: * @see #PALFileLoader(String, int)
078: */
079: public PALFileLoader(File file) {
080: if (!file.exists() | !file.canRead())
081: throw new IllegalArgumentException(
082: "The provided file cannot be read.");
083: BufferedReader reader = null;
084: try {
085: reader = new BufferedReader(new FileReader(file));
086: // header
087: boolean loadNext = false;
088: String temp = reader.readLine().trim();
089: if (temp.equalsIgnoreCase("JASC-PAL")) {
090: if (LOGGER.isLoggable(Level.FINE))
091: LOGGER.fine("Found header in palette file");
092: loadNext = true;
093: }
094:
095: // version
096: if (loadNext) {
097: temp = reader.readLine().trim();
098: if (temp.equalsIgnoreCase("0100")) {
099: if (LOGGER.isLoggable(Level.FINE))
100: LOGGER.fine("Found version in palette file");
101: loadNext = true;
102: }
103: }
104:
105: // num colors
106: if (loadNext)
107: temp = reader.readLine().trim();
108:
109: this .mapsize = Integer.parseInt(temp);
110: if (mapsize > 256 || mapsize <= 0)
111: throw new IllegalArgumentException(
112: "The provided number of colors is invalid");
113:
114: // load various colors
115: final byte colorMap[][] = new byte[3][mapsize < 256 ? mapsize + 1
116: : mapsize];
117: for (int i = 0; i < mapsize; i++) {
118: // get the line
119: temp = reader.readLine().trim();
120:
121: if (temp.startsWith("#"))
122: temp = "0x" + temp.substring(1);
123:
124: if (temp.startsWith("0x") || temp.startsWith("0X")) {
125: final Color color = Color.decode(temp);
126: colorMap[0][i] = (byte) color.getRed();
127: colorMap[1][i] = (byte) color.getGreen();
128: colorMap[2][i] = (byte) color.getBlue();
129: } else {
130: // tokenize it
131: final StringTokenizer tokenizer = new StringTokenizer(
132: temp, " ", false);
133: int numComponents = 0;
134: while (tokenizer.hasMoreTokens()) {
135: if (numComponents >= 3)
136: throw new IllegalArgumentException(
137: "The number of components in one the color is greater than 3!");
138: colorMap[numComponents++][i] = (byte) Integer
139: .parseInt(tokenizer.nextToken());
140:
141: }
142: if (numComponents != 3)
143: throw new IllegalArgumentException(
144: "The number of components in one the color is invalid!");
145: }
146: }
147:
148: // //
149: //
150: // create the index color model reserving space for the transparent
151: // pixel is room exists.
152: //
153: ////
154: if (mapsize < 256)
155: this .indexColorModel = new IndexColorModel(8,
156: mapsize + 1, colorMap[0], colorMap[1],
157: colorMap[2], mapsize);
158: else
159: this .indexColorModel = new IndexColorModel(8, mapsize,
160: colorMap[0], colorMap[1], colorMap[2]);
161: } catch (FileNotFoundException e) {
162: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
163: } catch (IOException e) {
164: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
165:
166: } catch (Exception e) {
167: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
168: } finally {
169: if (reader != null)
170: try {
171: reader.close();
172: } catch (IOException e) {
173: LOGGER.log(Level.INFO, e.getLocalizedMessage(), e);
174: }
175: }
176:
177: }
178:
179: public IndexColorModel getIndexColorModel() {
180: return indexColorModel;
181: }
182:
183: public int getMapsize() {
184: return mapsize;
185: }
186: }
|