001: /*
002: * $RCSfile: MlibDFTOpImage.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:55:53 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.mlib;
013:
014: import java.awt.Rectangle;
015: import java.awt.geom.Point2D;
016: import java.awt.image.BandedSampleModel;
017: import java.awt.image.ColorModel;
018: import java.awt.image.ComponentSampleModel;
019: import java.awt.image.DataBuffer;
020: import java.awt.image.Raster;
021: import java.awt.image.RenderedImage;
022: import java.awt.image.SampleModel;
023: import java.awt.image.WritableRaster;
024: import java.util.Map;
025: import javax.media.jai.EnumeratedParameter;
026: import javax.media.jai.ImageLayout;
027: import javax.media.jai.JAI;
028: import javax.media.jai.RasterFactory;
029: import javax.media.jai.UntiledOpImage;
030: import javax.media.jai.operator.DFTDescriptor;
031: import com.sun.media.jai.util.JDKWorkarounds;
032: import com.sun.media.jai.util.MathJAI;
033: import com.sun.medialib.mlib.*;
034:
035: /**
036: * An OpImage class that performs a "DFT" and "IDFT" operations.
037: *
038: */
039: final class MlibDFTOpImage extends UntiledOpImage {
040: /** The mediaLib mode of the Fourier transform. */
041: private int DFTMode;
042:
043: /**
044: * Override the dimension specification for the destination such that it
045: * has width and height which are equal to non-negative powers of 2.
046: */
047: private static ImageLayout layoutHelper(ImageLayout layout,
048: RenderedImage source, EnumeratedParameter dataNature) {
049: // Set the complex flags for source and destination.
050: boolean isComplexSource = !dataNature
051: .equals(DFTDescriptor.REAL_TO_COMPLEX);
052: boolean isComplexDest = !dataNature
053: .equals(DFTDescriptor.COMPLEX_TO_REAL);
054:
055: // Get the number of source bands.
056: SampleModel srcSampleModel = source.getSampleModel();
057: int numSourceBands = srcSampleModel.getNumBands();
058:
059: // Check for mediaLib support of this source.
060: if ((isComplexSource && numSourceBands != 2)
061: || (!isComplexSource && numSourceBands != 1)) {
062: // This should never occur due to checks in
063: // MlibDFTRIF and MlibIDFTRIF.
064: throw new RuntimeException(JaiI18N
065: .getString("MlibDFTOpImage0"));
066: }
067:
068: // Create an ImageLayout or clone the one passed in.
069: ImageLayout il = layout == null ? new ImageLayout()
070: : (ImageLayout) layout.clone();
071:
072: // Force the origin to coincide with that of the source.
073: il.setMinX(source.getMinX());
074: il.setMinY(source.getMinY());
075:
076: // Recalculate the non-unity dimensions to be a positive power of 2.
077: // XXX This calculation should not be effected if an implementation
078: // of the FFT which supports arbitrary dimensions is used.
079: int currentWidth = il.getWidth(source);
080: int currentHeight = il.getHeight(source);
081: int newWidth;
082: int newHeight;
083: if (currentWidth == 1 && currentHeight == 1) {
084: newWidth = newHeight = 1;
085: } else if (currentWidth == 1 && currentHeight > 1) {
086: newWidth = 1;
087: newHeight = MathJAI.nextPositivePowerOf2(currentHeight);
088: } else if (currentWidth > 1 && currentHeight == 1) {
089: newWidth = MathJAI.nextPositivePowerOf2(currentWidth);
090: newHeight = 1;
091: } else { // Neither dimension equal to unity.
092: newWidth = MathJAI.nextPositivePowerOf2(currentWidth);
093: newHeight = MathJAI.nextPositivePowerOf2(currentHeight);
094: }
095: il.setWidth(newWidth);
096: il.setHeight(newHeight);
097:
098: // Initialize the SampleModel creation flag.
099: boolean createNewSampleModel = false;
100:
101: // Determine the number of required bands.
102: int requiredNumBands = numSourceBands;
103: if (isComplexSource && !isComplexDest) {
104: requiredNumBands /= 2;
105: } else if (!isComplexSource && isComplexDest) {
106: requiredNumBands *= 2;
107: }
108:
109: // Set the number of bands.
110: SampleModel sm = il.getSampleModel(source);
111: int numBands = sm.getNumBands();
112: if (numBands != requiredNumBands) {
113: numBands = requiredNumBands;
114: createNewSampleModel = true;
115: }
116:
117: // Force the image to contain floating point data.
118: int dataType = sm.getTransferType();
119: if (dataType != DataBuffer.TYPE_FLOAT
120: && dataType != DataBuffer.TYPE_DOUBLE) {
121: dataType = DataBuffer.TYPE_FLOAT;
122: createNewSampleModel = true;
123: }
124:
125: // Create a new SampleModel for the destination if necessary.
126: if (createNewSampleModel) {
127: int[] bandOffsets = new int[numBands];
128: // Force the band offsets to be monotonically increasing as
129: // mediaLib expects the real part to be in band 0 and the
130: // imaginary part in band 1.
131: for (int b = 0; b < numBands; b++) {
132: bandOffsets[b] = b;
133: }
134:
135: int lineStride = newWidth * numBands;
136: sm = RasterFactory.createPixelInterleavedSampleModel(
137: dataType, newWidth, newHeight, numBands,
138: lineStride, bandOffsets);
139: il.setSampleModel(sm);
140:
141: // Clear the ColorModel mask if needed.
142: ColorModel cm = il.getColorModel(null);
143: if (cm != null
144: && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
145: // Clear the mask bit if incompatible.
146: il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
147: }
148: }
149:
150: return il;
151: }
152:
153: /**
154: * Constructs an MlibDFTOpImage.
155: *
156: * @param source a RenderedImage.
157:
158: * @param layout an ImageLayout optionally containing the tile
159: * grid layout, SampleModel, and ColorModel, or null.
160: * @param dataNature the nature of the source/destination data transform
161: * (real->complex, etc.).
162: * @param isForward whether the transform is forward (negative exponent).
163: * @param scaleType the scaling type of the transform.
164: */
165: public MlibDFTOpImage(RenderedImage source, Map config,
166: ImageLayout layout, EnumeratedParameter dataNature,
167: boolean isForward, EnumeratedParameter scaleType) {
168: super (source, config, layoutHelper(layout, source, dataNature));
169:
170: if (scaleType.equals(DFTDescriptor.SCALING_NONE)) {
171: DFTMode = isForward ? Image.MLIB_DFT_SCALE_NONE
172: : Image.MLIB_IDFT_SCALE_NONE;
173: } else if (scaleType.equals(DFTDescriptor.SCALING_UNITARY)) {
174: DFTMode = isForward ? Image.MLIB_DFT_SCALE_SQRT
175: : Image.MLIB_IDFT_SCALE_SQRT;
176: } else if (scaleType.equals(DFTDescriptor.SCALING_DIMENSIONS)) {
177: DFTMode = isForward ? Image.MLIB_DFT_SCALE_MXN
178: : Image.MLIB_IDFT_SCALE_MXN;
179: } else {
180: // This should never occur due to checks in DFTDescriptor and
181: // IDFTDescriptor.
182: throw new RuntimeException(JaiI18N
183: .getString("MlibDFTOpImage1"));
184: }
185: }
186:
187: /*
188: * This method is required as a workaround to the way MediaLibAccessor
189: * works. MediaLibAccessor handles ComponentSampleModels differently
190: * from other SampleModels. It does a test of whether the data are
191: * consecutive without respect to the band ordering. This assumes that
192: * the operation is independent of band ordering. This is not true for
193: * the Fourier transform as the real and imaginary parts are in bands
194: * 1 and 2, respectively.
195: */
196: public static boolean isAcceptableSampleModel(SampleModel sm) {
197: if (!(sm instanceof ComponentSampleModel)) {
198: return true;
199: }
200:
201: ComponentSampleModel csm = (ComponentSampleModel) sm;
202:
203: int[] bandOffsets = csm.getBandOffsets();
204:
205: if (bandOffsets.length == 2
206: && bandOffsets[1] == bandOffsets[0] + 1) {
207: return true;
208: }
209:
210: return false;
211: }
212:
213: /**
214: * Computes the source point corresponding to the supplied point.
215: *
216: * @param destPt the position in destination image coordinates
217: * to map to source image coordinates.
218: *
219: * @return <code>null</code>.
220: *
221: * @throws IllegalArgumentException if <code>destPt</code> is
222: * <code>null</code>.
223: *
224: * @since JAI 1.1.2
225: */
226: public Point2D mapDestPoint(Point2D destPt) {
227: if (destPt == null) {
228: throw new IllegalArgumentException(JaiI18N
229: .getString("Generic0"));
230: }
231:
232: return null;
233: }
234:
235: /**
236: * Computes the destination point corresponding to the supplied point.
237: *
238: * @return <code>null</code>.
239: *
240: * @throws IllegalArgumentException if <code>sourcePt</code> is
241: * <code>null</code>.
242: *
243: * @since JAI 1.1.2
244: */
245: public Point2D mapSourcePoint(Point2D sourcePt) {
246: if (sourcePt == null) {
247: throw new IllegalArgumentException(JaiI18N
248: .getString("Generic0"));
249: }
250:
251: return null;
252: }
253:
254: /**
255: * Calculate the destination image from the source image.
256: *
257: * @param source The source Raster; should be the whole image.
258: * @param dest The destination WritableRaster; should be the whole image.
259: * @param destRect The destination Rectangle; should equal the destination
260: * image bounds.
261: */
262: protected void computeImage(Raster[] sources, WritableRaster dest,
263: Rectangle destRect) {
264: Raster source = sources[0];
265:
266: int formatTag = MediaLibAccessor.findCompatibleTag(
267: new Raster[] { source }, dest);
268:
269: MediaLibAccessor srcAccessor = new MediaLibAccessor(source,
270: mapDestRect(destRect, 0), formatTag);
271: MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,
272: destRect, formatTag);
273:
274: mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
275: mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
276:
277: for (int i = 0; i < dstML.length; i++) {
278: Image.FourierTransform(dstML[i], srcML[i], DFTMode);
279: }
280:
281: if (dstAccessor.isDataCopy()) {
282: dstAccessor.clampDataArrays();
283: dstAccessor.copyDataToRaster();
284: }
285: }
286: }
|