001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.coverage.grid;
018:
019: // J2SE dependencies
020: import java.awt.image.DataBuffer;
021: import java.util.Arrays;
022: import java.util.Map;
023:
024: // JAI dependencies
025: import javax.media.jai.LookupTableJAI;
026:
027: // OpenGIS dependencies
028: import org.opengis.referencing.operation.MathTransform1D;
029: import org.opengis.referencing.operation.TransformException;
030:
031: // Geotools dependencies
032: import org.geotools.util.WeakValueHashMap;
033:
034: /**
035: * A factory for {@link LookupTableJAI} objects built from an array of {@link MathTransform1D}.
036: * This factory is used internally by {@link GridCoverage#createGeophysics}.
037: *
038: * @since 2.1
039: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/grid/LookupTableFactory.java $
040: * @version $Id: LookupTableFactory.java 23120 2006-11-29 16:44:07Z desruisseaux $
041: * @author Martin Desruisseaux
042: */
043: final class LookupTableFactory {
044: /**
045: * The pool of {@link LookupTableJAI} objects already created.
046: */
047: private static final Map pool = new WeakValueHashMap();
048:
049: /**
050: * The source data type. Should be one of {@link DataBuffer} constants.
051: */
052: private final int sourceType;
053:
054: /**
055: * The target data type. Should be one of {@link DataBuffer} constants.
056: */
057: private final int targetType;
058:
059: /**
060: * The math transforms for this key.
061: */
062: private final MathTransform1D[] transforms;
063:
064: /**
065: * Create a new objet to use as a key in the {@link #pool}.
066: *
067: * @param sourceType The source data type. Should be one of {@link DataBuffer} constants.
068: * @param targetType The target data type. Should be one of {@link DataBuffer} constants.
069: * @param transforms The math transforms to apply.
070: */
071: private LookupTableFactory(final int sourceType,
072: final int targetType, final MathTransform1D[] transforms) {
073: this .sourceType = sourceType;
074: this .targetType = targetType;
075: this .transforms = transforms;
076: }
077:
078: /**
079: * Gets a lookup factory
080: *
081: * @param sourceType The source data type. Should be one of {@link DataBuffer} constants.
082: * @param targetType The target data type. Should be one of {@link DataBuffer} constants.
083: * @param transforms The math transforms to apply.
084: * @return The lookup table, or {@code null} if this method can't build a lookup
085: * table for the supplied parameters.
086: * @throws TransformException if a transformation failed.
087: */
088: public static LookupTableJAI create(final int sourceType,
089: final int targetType, final MathTransform1D[] transforms)
090: throws TransformException {
091: /*
092: * Argument check. Null values are legal but can't be processed by this method.
093: */
094: final int nbands = transforms.length;
095: for (int i = 0; i < nbands; i++) {
096: if (transforms[i] == null) {
097: return null;
098: }
099: }
100: synchronized (pool) {
101: /*
102: * Check if a table is already available in the cache. Since tables may be 64 ko big,
103: * sharing tables may save a significant amount of memory if there is many images.
104: */
105: final LookupTableFactory key = new LookupTableFactory(
106: sourceType, targetType, transforms);
107: LookupTableJAI table = (LookupTableJAI) pool.get(key);
108: if (table != null) {
109: return table;
110: }
111: /*
112: * Compute the table's size according the source datatype. For datatype 'short' (signed
113: * or unsigned), we will create the table only if the target datatype is 'byte' in order
114: * to avoid to use too much memory for the table. The memory consumed for a table from
115: * source datatype 'short' to target datatype 'byte' is 64 ko.
116: */
117: final int length;
118: final int offset;
119: switch (sourceType) {
120: default: {
121: return null;
122: }
123: case DataBuffer.TYPE_BYTE: {
124: length = 0x100;
125: offset = 0;
126: break;
127: }
128: case DataBuffer.TYPE_SHORT:
129: case DataBuffer.TYPE_USHORT: {
130: if (targetType != DataBuffer.TYPE_BYTE) {
131: // Avoid to use too much memory for the table.
132: return null;
133: }
134: length = 0x10000;
135: offset = (sourceType == DataBuffer.TYPE_SHORT) ? Short.MIN_VALUE
136: : 0;
137: break;
138: }
139: }
140: /*
141: * Build the table according the target datatype.
142: */
143: switch (targetType) {
144: default: {
145: return null;
146: }
147:
148: case DataBuffer.TYPE_DOUBLE: {
149: final double[][] data = new double[nbands][];
150: final double[] buffer = new double[length];
151: for (int i = length; --i >= 0;) {
152: buffer[i] = i;
153: }
154: for (int i = nbands; --i >= 0;) {
155: final double[] array = (i == 0) ? buffer
156: : (double[]) buffer.clone();
157: transforms[i].transform(array, 0, array, 0,
158: array.length);
159: data[i] = array;
160: }
161: table = new LookupTableJAI(data, offset);
162: break;
163: }
164:
165: case DataBuffer.TYPE_FLOAT: {
166: final float[][] data = new float[nbands][];
167: final float[] buffer = new float[length];
168: for (int i = length; --i >= 0;) {
169: buffer[i] = i;
170: }
171: for (int i = transforms.length; --i >= 0;) {
172: final float[] array = (i == 0) ? buffer
173: : (float[]) buffer.clone();
174: transforms[i].transform(array, 0, array, 0, length);
175: data[i] = array;
176: }
177: table = new LookupTableJAI(data, offset);
178: break;
179: }
180:
181: case DataBuffer.TYPE_INT: {
182: final int[][] data = new int[nbands][];
183: for (int i = nbands; --i >= 0;) {
184: final MathTransform1D tr = transforms[i];
185: final int[] array = new int[length];
186: for (int j = length; --j >= 0;) {
187: array[j] = (int) Math.min(Math.max(Math
188: .round(tr.transform(j + offset)),
189: Integer.MIN_VALUE), Integer.MAX_VALUE);
190: }
191: data[i] = array;
192: }
193: table = new LookupTableJAI(data, offset);
194: break;
195: }
196:
197: case DataBuffer.TYPE_SHORT:
198: case DataBuffer.TYPE_USHORT: {
199: final int minimum, maximum;
200: if (targetType == DataBuffer.TYPE_SHORT) {
201: minimum = Short.MIN_VALUE;
202: maximum = Short.MAX_VALUE;
203: } else {
204: minimum = 0;
205: maximum = 0xFFFF;
206: }
207: final short[][] data = new short[nbands][];
208: for (int i = nbands; --i >= 0;) {
209: final MathTransform1D tr = transforms[i];
210: final short[] array = new short[length];
211: for (int j = length; --j >= 0;) {
212: array[j] = (short) Math.min(Math.max(Math
213: .round(tr.transform(j + offset)),
214: minimum), maximum);
215: }
216: data[i] = array;
217: }
218: table = new LookupTableJAI(data, offset, minimum != 0);
219: break;
220: }
221:
222: case DataBuffer.TYPE_BYTE: {
223: final byte[][] data = new byte[nbands][];
224: for (int i = nbands; --i >= 0;) {
225: final MathTransform1D tr = transforms[i];
226: final byte[] array = new byte[length];
227: for (int j = length; --j >= 0;) {
228: array[j] = (byte) Math.min(Math.max(Math
229: .round(tr.transform(j + offset)), 0),
230: 0xFF);
231: }
232: data[i] = array;
233: }
234: table = new LookupTableJAI(data, offset);
235: break;
236: }
237: }
238: pool.put(key, table);
239: return table;
240: }
241: }
242:
243: /**
244: * Returns a hash code value for this key. This is for internal use by
245: * {@code LookupTableFactory} and is public only as an implementation side effect.
246: */
247: public int hashCode() {
248: int code = sourceType + 37 * targetType;
249: final int length = transforms.length;
250: for (int i = 0; i < length; i++) {
251: code = code * 37 + transforms[i].hashCode();
252: }
253: return code;
254: }
255:
256: /**
257: * Compare the specified object with this key for equality. This is for internal use by
258: * {@code LookupTableFactory} and is public only as an implementation side effect.
259: */
260: public boolean equals(final Object other) {
261: if (other instanceof LookupTableFactory) {
262: final LookupTableFactory that = (LookupTableFactory) other;
263: return this .sourceType == that.sourceType
264: && this .targetType == that.targetType
265: && Arrays.equals(this .transforms, that.transforms);
266: }
267: return false;
268: }
269: }
|