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.resources.image;
018:
019: // J2SE dependencies
020: import java.awt.image.BandedSampleModel;
021: import java.awt.image.ComponentSampleModel;
022: import java.awt.image.DataBuffer;
023: import java.awt.image.IndexColorModel;
024: import java.awt.image.Raster;
025: import java.awt.image.SampleModel;
026: import java.awt.image.WritableRaster;
027: import java.util.Arrays;
028:
029: /**
030: * An {@link IndexColorModel} tolerant with image having more than one band.
031: * <p>
032: * <strong>Reminder:</strong> {@link #getNumComponents} will returns 3 or 4 no matter
033: * how many bands were specified to the constructor. This is not specific to this class;
034: * {@code IndexColorModel} behave that way. So we can't rely on this method for checking
035: * the number of bands.
036: *
037: * @since 2.0
038: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/resources/image/MultiBandsIndexColorModel.java $
039: * @version $Id: MultiBandsIndexColorModel.java 20970 2006-08-11 07:53:22Z jgarnett $
040: * @author Martin Desruisseaux
041: * @author Andrea Aime
042: */
043: final class MultiBandsIndexColorModel extends IndexColorModel {
044: /**
045: * The number of bands.
046: */
047: final int numBands;
048:
049: /**
050: * The visible band.
051: */
052: private final int visibleBand;
053:
054: /**
055: * Construct an object with the specified properties.
056: *
057: * @param bits The number of bits each pixel occupies.
058: * @param size The size of the color component arrays.
059: * @param cmap The array of color components.
060: * @param start The starting offset of the first color component.
061: * @param hasalpha Indicates whether alpha values are contained in the {@code cmap} array.
062: * @param transparent The index of the fully transparent pixel.
063: * @param transferType The data type of the array used to represent pixel values. The
064: * data type must be either {@code DataBuffer.TYPE_BYTE} or
065: * {@code DataBuffer.TYPE_USHORT}.
066: * @param numBands The number of bands.
067: * @param visibleBands The band to display.
068: *
069: * @throws IllegalArgumentException if {@code bits} is less than 1 or greater than 16.
070: * @throws IllegalArgumentException if {@code size} is less than 1.
071: * @throws IllegalArgumentException if {@code transferType} is not one of
072: * {@code DataBuffer.TYPE_BYTE} or {@code DataBuffer.TYPE_USHORT}.
073: */
074: public MultiBandsIndexColorModel(final int bits, final int size,
075: final int[] cmap, final int start, final boolean hasAlpha,
076: final int transparent, final int transferType,
077: final int numBands, final int visibleBand) {
078: super (bits, size, cmap, start, hasAlpha, transparent,
079: transferType);
080: this .numBands = numBands;
081: this .visibleBand = visibleBand;
082: }
083:
084: /**
085: * Returns a data element array representation of a pixel in this color model,
086: * given an integer pixel representation in the default RGB color model.
087: * <p>
088: * This method returns an array with a length equals to the number of bands specified to
089: * the constructor ({@code IndexColorModel} would returns an array of length 1). All array
090: * elements are set to the same value. Replicating the pixel value is a somewhat arbitrary
091: * choice, but this choice make this image appears as a gray scale image if the underlying
092: * {@link DataBuffer} were displayed again with a RGB color model instead of this one. Such
093: * a gray scale image seems more neutral than an image where only the Red component would vary.
094: * <p>
095: * All other {@code getDataElement} methods in this color model are ultimately defined in terms
096: * of this method, so overriding this method should be enough.
097: */
098: public Object getDataElements(final int RGB, Object pixel) {
099: if (pixel == null) {
100: switch (transferType) {
101: case DataBuffer.TYPE_SHORT: // fall through
102: case DataBuffer.TYPE_USHORT:
103: pixel = new short[numBands];
104: break;
105: case DataBuffer.TYPE_BYTE:
106: pixel = new byte[numBands];
107: break;
108: case DataBuffer.TYPE_INT:
109: pixel = new int[numBands];
110: break;
111: default:
112: throw new UnsupportedOperationException(unsupported());
113: }
114: }
115: pixel = super .getDataElements(RGB, pixel);
116: switch (transferType) {
117: case DataBuffer.TYPE_SHORT: // fall through
118: case DataBuffer.TYPE_USHORT: {
119: final short[] array = (short[]) pixel;
120: Arrays.fill(array, 1, numBands, array[0]);
121: break;
122: }
123: case DataBuffer.TYPE_BYTE: {
124: final byte[] array = (byte[]) pixel;
125: Arrays.fill(array, 1, numBands, array[0]);
126: break;
127: }
128: case DataBuffer.TYPE_INT: {
129: final int[] array = (int[]) pixel;
130: Arrays.fill(array, 1, numBands, array[0]);
131: break;
132: }
133: default:
134: throw new UnsupportedOperationException(unsupported());
135: }
136: return pixel;
137: }
138:
139: /**
140: * Returns an array of unnormalized color/alpha components for a specified pixel
141: * in this color model. This method is the converse of {@link #getDataElements}.
142: */
143: public int[] getComponents(final Object pixel,
144: final int[] components, final int offset) {
145: final int i;
146: switch (transferType) {
147: case DataBuffer.TYPE_SHORT: // Fall through
148: case DataBuffer.TYPE_USHORT:
149: i = ((short[]) pixel)[visibleBand] & 0xffff;
150: break;
151: case DataBuffer.TYPE_BYTE:
152: i = ((byte[]) pixel)[visibleBand] & 0xff;
153: break;
154: case DataBuffer.TYPE_INT:
155: i = ((int[]) pixel)[visibleBand];
156: break;
157: default:
158: throw new UnsupportedOperationException(unsupported());
159: }
160: return getComponents(i, components, offset);
161: }
162:
163: /**
164: * Returns the red color component for the specified pixel, scaled
165: * from 0 to 255 in the default RGB {@code ColorSpace}, sRGB.
166: */
167: public int getRed(final Object inData) {
168: final int pixel;
169: switch (transferType) {
170: case DataBuffer.TYPE_BYTE: {
171: pixel = ((byte[]) inData)[visibleBand] & 0xff;
172: break;
173: }
174: case DataBuffer.TYPE_USHORT: {
175: pixel = ((short[]) inData)[visibleBand] & 0xffff;
176: break;
177: }
178: case DataBuffer.TYPE_INT: {
179: pixel = ((int[]) inData)[visibleBand];
180: break;
181: }
182: default: {
183: throw new UnsupportedOperationException(unsupported());
184: }
185: }
186: return getRed(pixel);
187: }
188:
189: /**
190: * Returns the green color component for the specified pixel, scaled
191: * from 0 to 255 in the default RGB {@code ColorSpace}, sRGB.
192: */
193: public int getGreen(final Object inData) {
194: final int pixel;
195: switch (transferType) {
196: case DataBuffer.TYPE_BYTE: {
197: pixel = ((byte[]) inData)[visibleBand] & 0xff;
198: break;
199: }
200: case DataBuffer.TYPE_USHORT: {
201: pixel = ((short[]) inData)[visibleBand] & 0xffff;
202: break;
203: }
204: case DataBuffer.TYPE_INT: {
205: pixel = ((int[]) inData)[visibleBand];
206: break;
207: }
208: default: {
209: throw new UnsupportedOperationException(unsupported());
210: }
211: }
212: return getGreen(pixel);
213: }
214:
215: /**
216: * Returns the blue color component for the specified pixel, scaled
217: * from 0 to 255 in the default RGB {@code ColorSpace}, sRGB.
218: */
219: public int getBlue(final Object inData) {
220: final int pixel;
221: switch (transferType) {
222: case DataBuffer.TYPE_BYTE: {
223: pixel = ((byte[]) inData)[visibleBand] & 0xff;
224: break;
225: }
226: case DataBuffer.TYPE_USHORT: {
227: pixel = ((short[]) inData)[visibleBand] & 0xffff;
228: break;
229: }
230: case DataBuffer.TYPE_INT: {
231: pixel = ((int[]) inData)[visibleBand];
232: break;
233: }
234: default: {
235: throw new UnsupportedOperationException(unsupported());
236: }
237: }
238: return getBlue(pixel);
239: }
240:
241: /**
242: * Returns the alpha component for the specified pixel, scaled from 0 to 255.
243: */
244: public int getAlpha(final Object inData) {
245: final int pixel;
246: switch (transferType) {
247: case DataBuffer.TYPE_BYTE: {
248: pixel = ((byte[]) inData)[visibleBand] & 0xff;
249: break;
250: }
251: case DataBuffer.TYPE_USHORT: {
252: pixel = ((short[]) inData)[visibleBand] & 0xffff;
253: break;
254: }
255: case DataBuffer.TYPE_INT: {
256: pixel = ((int[]) inData)[visibleBand];
257: break;
258: }
259: default: {
260: throw new UnsupportedOperationException(unsupported());
261: }
262: }
263: return getAlpha(pixel);
264: }
265:
266: /**
267: * Returns an error message for unsupported operation exception.
268: */
269: private String unsupported() {
270: return "This method has not been implemented for transferType "
271: + transferType;
272: }
273:
274: /**
275: * Creates a {@code WritableRaster} with the specified width
276: * and height that has a data layout ({@code SampleModel})
277: * compatible with this {@code ColorModel}.
278: */
279: public WritableRaster createCompatibleWritableRaster(
280: final int width, final int height) {
281: return Raster.createBandedRaster(transferType, width, height,
282: numBands, null);
283: }
284:
285: /**
286: * Returns {@code true} if {@code raster} is compatible
287: * with this {@code ColorModel}.
288: */
289: public boolean isCompatibleRaster(final Raster raster) {
290: return isCompatibleSampleModel(raster.getSampleModel());
291: }
292:
293: /**
294: * Creates a {@code SampleModel} with the specified
295: * width and height that has a data layout compatible with
296: * this {@code ColorModel}.
297: */
298: public SampleModel createCompatibleSampleModel(final int width,
299: final int height) {
300: return new BandedSampleModel(transferType, width, height,
301: numBands);
302: }
303:
304: /**
305: * Checks if the specified {@code SampleModel} is compatible
306: * with this {@code ColorModel}.
307: */
308: public boolean isCompatibleSampleModel(final SampleModel sm) {
309: return (sm instanceof ComponentSampleModel)
310: && sm.getTransferType() == transferType
311: && sm.getNumBands() == numBands
312: && (1 << sm.getSampleSize(visibleBand)) >= getMapSize();
313: }
314: }
|