001: /*
002: * $RCSfile: PolarToComplexOpImage.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:56:41 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.image.DataBuffer;
016: import java.awt.image.PixelInterleavedSampleModel;
017: import java.awt.image.Raster;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.SampleModel;
020: import java.awt.image.WritableRaster;
021: import javax.media.jai.ImageLayout;
022: import javax.media.jai.PointOpImage;
023: import javax.media.jai.RasterAccessor;
024: import javax.media.jai.RasterFormatTag;
025: import javax.media.jai.RasterFactory;
026: import java.util.Map;
027: import com.sun.media.jai.util.ImageUtil;
028: import com.sun.media.jai.util.JDKWorkarounds;
029:
030: /// XXX Testing
031: /// import javax.media.jai.TiledImage;
032: /// import javax.media.jai.JAI;
033:
034: /**
035: * An <code>OpImage</code> implementing the "PolarToComplex" operation
036: * as described in
037: * <code>javax.media.jai.operator.PolarToComplexDescriptor</code>.
038: *
039: * <p> The number of bands in the destination image is clamped to twice the
040: * minimum number of bands across all source images. The two source images
041: * are expected to be magnitude (first source) and phase (second source).
042: * If the phase image is integral, its values are assumed to lie in the
043: * range [0, MAX_VALUE] where MAX_VALUE is a functino of the data type.
044: * These values will be scaled to the range [-Math.PI, Math.PI] before being
045: * used.
046: *
047: * @since EA4
048: *
049: * @see javax.media.jai.PointOpImage
050: * @see javax.media.jai.operator.MagnitudeDescriptor
051: * @see javax.media.jai.operator.PhaseDescriptor
052: * @see javax.media.jai.operator.PolarToComplexDescriptor
053: *
054: */
055: final class PolarToComplexOpImage extends PointOpImage {
056: /** The gain to be applied to the phase. */
057: private double phaseGain = 1.0;
058:
059: /** The bias to be applied to the phase. */
060: private double phaseBias = 0.0;
061:
062: /**
063: * Constructs a <code>PolarToComplexOpImage</code> object.
064: *
065: * <p>The tile grid layout, SampleModel, and ColorModel may optionally
066: * be specified by an ImageLayout object.
067: *
068: * @param magnitude A RenderedImage representing magnitude.
069: * @param phase A RenderedImage representing phase.
070: * @param layout An ImageLayout optionally containing the tile grid layout,
071: * SampleModel, and ColorModel, or null.
072: */
073: public PolarToComplexOpImage(RenderedImage magnitude,
074: RenderedImage phase, Map config, ImageLayout layout) {
075: super (magnitude, phase, layout, config, true);
076:
077: // Force the number of bands to be twice the minimum source band count.
078: int numBands = 2 * Math.min(magnitude.getSampleModel()
079: .getNumBands(), phase.getSampleModel().getNumBands());
080: if (sampleModel.getNumBands() != numBands) {
081: // Create a new SampleModel for the destination.
082: sampleModel = RasterFactory.createComponentSampleModel(
083: sampleModel, sampleModel.getTransferType(),
084: sampleModel.getWidth(), sampleModel.getHeight(),
085: numBands);
086:
087: if (colorModel != null
088: && !JDKWorkarounds.areCompatibleDataModels(
089: sampleModel, colorModel)) {
090: colorModel = ImageUtil.getCompatibleColorModel(
091: sampleModel, config);
092: }
093: }
094:
095: // Set phase gain and bias as a function of the phase image data type.
096: switch (phase.getSampleModel().getTransferType()) {
097: case DataBuffer.TYPE_BYTE:
098: phaseGain = (2.0 * Math.PI) / 255.0;
099: phaseBias = -Math.PI;
100: break;
101: case DataBuffer.TYPE_SHORT:
102: phaseGain = (2.0 * Math.PI) / Short.MAX_VALUE;
103: phaseBias = -Math.PI;
104: break;
105: case DataBuffer.TYPE_USHORT:
106: phaseGain = (2.0 * Math.PI)
107: / (Short.MAX_VALUE - Short.MIN_VALUE);
108: phaseBias = -Math.PI;
109: break;
110: case DataBuffer.TYPE_INT:
111: phaseGain = (2.0 * Math.PI) / Integer.MAX_VALUE;
112: phaseBias = -Math.PI;
113: break;
114: default:
115: // A floating point type: do nothing - use class defaults.
116: }
117:
118: // TODO: Set "complex" property.
119: }
120:
121: /*
122: * Calculate a complex rectangle given the magnitude and phase.
123: *
124: * @param sources Cobbled sources, guaranteed to provide all the
125: * source data necessary for computing the rectangle.
126: * @param dest The tile containing the rectangle to be computed.
127: * @param destRect The rectangle within the tile to be computed.
128: */
129: protected void computeRect(Raster[] sources, WritableRaster dest,
130: Rectangle destRect) {
131: // Retrieve format tags.
132: RasterFormatTag[] formatTags = getFormatTags();
133:
134: // Construct RasterAccessors.
135: RasterAccessor magAccessor = new RasterAccessor(sources[0],
136: destRect, formatTags[0], getSource(0).getColorModel());
137: RasterAccessor phsAccessor = new RasterAccessor(sources[1],
138: destRect, formatTags[1], getSource(1).getColorModel());
139: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
140: formatTags[2], getColorModel());
141:
142: // Branch to the method appropriate to the accessor data type.
143: switch (dstAccessor.getDataType()) {
144: case DataBuffer.TYPE_BYTE:
145: computeRectByte(magAccessor, phsAccessor, dstAccessor,
146: destRect.height, destRect.width);
147: break;
148: case DataBuffer.TYPE_SHORT:
149: computeRectShort(magAccessor, phsAccessor, dstAccessor,
150: destRect.height, destRect.width);
151: break;
152: case DataBuffer.TYPE_USHORT:
153: computeRectUShort(magAccessor, phsAccessor, dstAccessor,
154: destRect.height, destRect.width);
155: break;
156: case DataBuffer.TYPE_INT:
157: computeRectInt(magAccessor, phsAccessor, dstAccessor,
158: destRect.height, destRect.width);
159: break;
160: case DataBuffer.TYPE_FLOAT:
161: computeRectFloat(magAccessor, phsAccessor, dstAccessor,
162: destRect.height, destRect.width);
163: break;
164: case DataBuffer.TYPE_DOUBLE:
165: computeRectDouble(magAccessor, phsAccessor, dstAccessor,
166: destRect.height, destRect.width);
167: break;
168: default:
169: // NB: This statement should be unreachable.
170: throw new RuntimeException(JaiI18N
171: .getString("PolarToComplexOpImage0"));
172: }
173:
174: if (dstAccessor.needsClamping()) {
175: dstAccessor.clampDataArrays();
176: }
177:
178: // Make sure that the output data is copied to the destination.
179: dstAccessor.copyDataToRaster();
180: }
181:
182: private void computeRectDouble(RasterAccessor magAccessor,
183: RasterAccessor phsAccessor, RasterAccessor dstAccessor,
184: int numRows, int numCols) {
185: // Set pixel and line strides.
186: int dstPixelStride = dstAccessor.getPixelStride();
187: int dstScanlineStride = dstAccessor.getScanlineStride();
188: int magPixelStride = magAccessor.getPixelStride();
189: int magScanlineStride = magAccessor.getScanlineStride();
190: int phsPixelStride = phsAccessor.getPixelStride();
191: int phsScanlineStride = phsAccessor.getScanlineStride();
192:
193: // Loop over the destination components.
194: int numComponents = sampleModel.getNumBands() / 2;
195: for (int component = 0; component < numComponents; component++) {
196: // Set source band indices.
197: int dstBandReal = 2 * component;
198: int dstBandImag = dstBandReal + 1;
199:
200: // Get the source and destination arrays for this band.
201: double[] dstReal = dstAccessor
202: .getDoubleDataArray(dstBandReal);
203: double[] dstImag = dstAccessor
204: .getDoubleDataArray(dstBandImag);
205: double[] magData = magAccessor
206: .getDoubleDataArray(component);
207: double[] phsData = phsAccessor
208: .getDoubleDataArray(component);
209:
210: // Initialize the data offsets for this band.
211: int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal);
212: int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag);
213: int magOffset = magAccessor.getBandOffset(component);
214: int phsOffset = phsAccessor.getBandOffset(component);
215:
216: // Initialize the line offsets for looping.
217: int dstLineReal = dstOffsetReal;
218: int dstLineImag = dstOffsetImag;
219: int magLine = magOffset;
220: int phsLine = phsOffset;
221:
222: for (int row = 0; row < numRows; row++) {
223: // Initialize pixel offsets for this row.
224: int dstPixelReal = dstLineReal;
225: int dstPixelImag = dstLineImag;
226: int magPixel = magLine;
227: int phsPixel = phsLine;
228:
229: for (int col = 0; col < numCols; col++) {
230: double mag = magData[magPixel];
231: double phs = phsData[phsPixel] * phaseGain
232: + phaseBias;
233:
234: dstReal[dstPixelReal] = mag * Math.cos(phs);
235: dstImag[dstPixelImag] = mag * Math.sin(phs);
236:
237: dstPixelReal += dstPixelStride;
238: dstPixelImag += dstPixelStride;
239: magPixel += magPixelStride;
240: phsPixel += phsPixelStride;
241: }
242:
243: // Increment the line offsets.
244: dstLineReal += dstScanlineStride;
245: dstLineImag += dstScanlineStride;
246: magLine += magScanlineStride;
247: phsLine += phsScanlineStride;
248: }
249: }
250: }
251:
252: private void computeRectFloat(RasterAccessor magAccessor,
253: RasterAccessor phsAccessor, RasterAccessor dstAccessor,
254: int numRows, int numCols) {
255: // Set pixel and line strides.
256: int dstPixelStride = dstAccessor.getPixelStride();
257: int dstScanlineStride = dstAccessor.getScanlineStride();
258: int magPixelStride = magAccessor.getPixelStride();
259: int magScanlineStride = magAccessor.getScanlineStride();
260: int phsPixelStride = phsAccessor.getPixelStride();
261: int phsScanlineStride = phsAccessor.getScanlineStride();
262:
263: // Loop over the destination components.
264: int numComponents = sampleModel.getNumBands() / 2;
265: for (int component = 0; component < numComponents; component++) {
266: // Set source band indices.
267: int dstBandReal = 2 * component;
268: int dstBandImag = dstBandReal + 1;
269:
270: // Get the source and destination arrays for this band.
271: float[] dstReal = dstAccessor
272: .getFloatDataArray(dstBandReal);
273: float[] dstImag = dstAccessor
274: .getFloatDataArray(dstBandImag);
275: float[] magData = magAccessor.getFloatDataArray(component);
276: float[] phsData = phsAccessor.getFloatDataArray(component);
277:
278: // Initialize the data offsets for this band.
279: int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal);
280: int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag);
281: int magOffset = magAccessor.getBandOffset(component);
282: int phsOffset = phsAccessor.getBandOffset(component);
283:
284: // Initialize the line offsets for looping.
285: int dstLineReal = dstOffsetReal;
286: int dstLineImag = dstOffsetImag;
287: int magLine = magOffset;
288: int phsLine = phsOffset;
289:
290: for (int row = 0; row < numRows; row++) {
291: // Initialize pixel offsets for this row.
292: int dstPixelReal = dstLineReal;
293: int dstPixelImag = dstLineImag;
294: int magPixel = magLine;
295: int phsPixel = phsLine;
296:
297: for (int col = 0; col < numCols; col++) {
298: double mag = magData[magPixel];
299: double phs = phsData[phsPixel] * phaseGain
300: + phaseBias;
301:
302: dstReal[dstPixelReal] = ImageUtil.clampFloat(mag
303: * Math.cos(phs));
304: dstImag[dstPixelImag] = ImageUtil.clampFloat(mag
305: * Math.sin(phs));
306:
307: dstPixelReal += dstPixelStride;
308: dstPixelImag += dstPixelStride;
309: magPixel += magPixelStride;
310: phsPixel += phsPixelStride;
311: }
312:
313: // Increment the line offsets.
314: dstLineReal += dstScanlineStride;
315: dstLineImag += dstScanlineStride;
316: magLine += magScanlineStride;
317: phsLine += phsScanlineStride;
318: }
319: }
320: }
321:
322: private void computeRectInt(RasterAccessor magAccessor,
323: RasterAccessor phsAccessor, RasterAccessor dstAccessor,
324: int numRows, int numCols) {
325: // Set pixel and line strides.
326: int dstPixelStride = dstAccessor.getPixelStride();
327: int dstScanlineStride = dstAccessor.getScanlineStride();
328: int magPixelStride = magAccessor.getPixelStride();
329: int magScanlineStride = magAccessor.getScanlineStride();
330: int phsPixelStride = phsAccessor.getPixelStride();
331: int phsScanlineStride = phsAccessor.getScanlineStride();
332:
333: // Loop over the destination components.
334: int numComponents = sampleModel.getNumBands() / 2;
335: for (int component = 0; component < numComponents; component++) {
336: // Set source band indices.
337: int dstBandReal = 2 * component;
338: int dstBandImag = dstBandReal + 1;
339:
340: // Get the source and destination arrays for this band.
341: int[] dstReal = dstAccessor.getIntDataArray(dstBandReal);
342: int[] dstImag = dstAccessor.getIntDataArray(dstBandImag);
343: int[] magData = magAccessor.getIntDataArray(component);
344: int[] phsData = phsAccessor.getIntDataArray(component);
345:
346: // Initialize the data offsets for this band.
347: int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal);
348: int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag);
349: int magOffset = magAccessor.getBandOffset(component);
350: int phsOffset = phsAccessor.getBandOffset(component);
351:
352: // Initialize the line offsets for looping.
353: int dstLineReal = dstOffsetReal;
354: int dstLineImag = dstOffsetImag;
355: int magLine = magOffset;
356: int phsLine = phsOffset;
357:
358: for (int row = 0; row < numRows; row++) {
359: // Initialize pixel offsets for this row.
360: int dstPixelReal = dstLineReal;
361: int dstPixelImag = dstLineImag;
362: int magPixel = magLine;
363: int phsPixel = phsLine;
364:
365: for (int col = 0; col < numCols; col++) {
366: double mag = magData[magPixel];
367: double phs = phsData[phsPixel] * phaseGain
368: + phaseBias;
369:
370: dstReal[dstPixelReal] = ImageUtil.clampRoundInt(mag
371: * Math.cos(phs));
372: dstImag[dstPixelImag] = ImageUtil.clampRoundInt(mag
373: * Math.sin(phs));
374:
375: dstPixelReal += dstPixelStride;
376: dstPixelImag += dstPixelStride;
377: magPixel += magPixelStride;
378: phsPixel += phsPixelStride;
379: }
380:
381: // Increment the line offsets.
382: dstLineReal += dstScanlineStride;
383: dstLineImag += dstScanlineStride;
384: magLine += magScanlineStride;
385: phsLine += phsScanlineStride;
386: }
387: }
388: }
389:
390: private void computeRectUShort(RasterAccessor magAccessor,
391: RasterAccessor phsAccessor, RasterAccessor dstAccessor,
392: int numRows, int numCols) {
393: // Set pixel and line strides.
394: int dstPixelStride = dstAccessor.getPixelStride();
395: int dstScanlineStride = dstAccessor.getScanlineStride();
396: int magPixelStride = magAccessor.getPixelStride();
397: int magScanlineStride = magAccessor.getScanlineStride();
398: int phsPixelStride = phsAccessor.getPixelStride();
399: int phsScanlineStride = phsAccessor.getScanlineStride();
400:
401: // Loop over the destination components.
402: int numComponents = sampleModel.getNumBands() / 2;
403: for (int component = 0; component < numComponents; component++) {
404: // Set source band indices.
405: int dstBandReal = 2 * component;
406: int dstBandImag = dstBandReal + 1;
407:
408: // Get the source and destination arrays for this band.
409: short[] dstReal = dstAccessor
410: .getShortDataArray(dstBandReal);
411: short[] dstImag = dstAccessor
412: .getShortDataArray(dstBandImag);
413: short[] magData = magAccessor.getShortDataArray(component);
414: short[] phsData = phsAccessor.getShortDataArray(component);
415:
416: // Initialize the data offsets for this band.
417: int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal);
418: int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag);
419: int magOffset = magAccessor.getBandOffset(component);
420: int phsOffset = phsAccessor.getBandOffset(component);
421:
422: // Initialize the line offsets for looping.
423: int dstLineReal = dstOffsetReal;
424: int dstLineImag = dstOffsetImag;
425: int magLine = magOffset;
426: int phsLine = phsOffset;
427:
428: for (int row = 0; row < numRows; row++) {
429: // Initialize pixel offsets for this row.
430: int dstPixelReal = dstLineReal;
431: int dstPixelImag = dstLineImag;
432: int magPixel = magLine;
433: int phsPixel = phsLine;
434:
435: for (int col = 0; col < numCols; col++) {
436: double mag = magData[magPixel] & 0xffff;
437: double phs = (phsData[phsPixel] & 0xffff)
438: * phaseGain + phaseBias;
439:
440: dstReal[dstPixelReal] = ImageUtil
441: .clampRoundUShort(mag * Math.cos(phs));
442: dstImag[dstPixelImag] = ImageUtil
443: .clampRoundUShort(mag * Math.sin(phs));
444:
445: dstPixelReal += dstPixelStride;
446: dstPixelImag += dstPixelStride;
447: magPixel += magPixelStride;
448: phsPixel += phsPixelStride;
449: }
450:
451: // Increment the line offsets.
452: dstLineReal += dstScanlineStride;
453: dstLineImag += dstScanlineStride;
454: magLine += magScanlineStride;
455: phsLine += phsScanlineStride;
456: }
457: }
458: }
459:
460: private void computeRectShort(RasterAccessor magAccessor,
461: RasterAccessor phsAccessor, RasterAccessor dstAccessor,
462: int numRows, int numCols) {
463: // Set pixel and line strides.
464: int dstPixelStride = dstAccessor.getPixelStride();
465: int dstScanlineStride = dstAccessor.getScanlineStride();
466: int magPixelStride = magAccessor.getPixelStride();
467: int magScanlineStride = magAccessor.getScanlineStride();
468: int phsPixelStride = phsAccessor.getPixelStride();
469: int phsScanlineStride = phsAccessor.getScanlineStride();
470:
471: // Loop over the destination components.
472: int numComponents = sampleModel.getNumBands() / 2;
473: for (int component = 0; component < numComponents; component++) {
474: // Set source band indices.
475: int dstBandReal = 2 * component;
476: int dstBandImag = dstBandReal + 1;
477:
478: // Get the source and destination arrays for this band.
479: short[] dstReal = dstAccessor
480: .getShortDataArray(dstBandReal);
481: short[] dstImag = dstAccessor
482: .getShortDataArray(dstBandImag);
483: short[] magData = magAccessor.getShortDataArray(component);
484: short[] phsData = phsAccessor.getShortDataArray(component);
485:
486: // Initialize the data offsets for this band.
487: int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal);
488: int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag);
489: int magOffset = magAccessor.getBandOffset(component);
490: int phsOffset = phsAccessor.getBandOffset(component);
491:
492: // Initialize the line offsets for looping.
493: int dstLineReal = dstOffsetReal;
494: int dstLineImag = dstOffsetImag;
495: int magLine = magOffset;
496: int phsLine = phsOffset;
497:
498: for (int row = 0; row < numRows; row++) {
499: // Initialize pixel offsets for this row.
500: int dstPixelReal = dstLineReal;
501: int dstPixelImag = dstLineImag;
502: int magPixel = magLine;
503: int phsPixel = phsLine;
504:
505: for (int col = 0; col < numCols; col++) {
506: double mag = magData[magPixel];
507: double phs = phsData[phsPixel] * phaseGain
508: + phaseBias;
509:
510: dstReal[dstPixelReal] = ImageUtil
511: .clampRoundShort(mag * Math.cos(phs));
512: dstImag[dstPixelImag] = ImageUtil
513: .clampRoundShort(mag * Math.sin(phs));
514:
515: dstPixelReal += dstPixelStride;
516: dstPixelImag += dstPixelStride;
517: magPixel += magPixelStride;
518: phsPixel += phsPixelStride;
519: }
520:
521: // Increment the line offsets.
522: dstLineReal += dstScanlineStride;
523: dstLineImag += dstScanlineStride;
524: magLine += magScanlineStride;
525: phsLine += phsScanlineStride;
526: }
527: }
528: }
529:
530: private void computeRectByte(RasterAccessor magAccessor,
531: RasterAccessor phsAccessor, RasterAccessor dstAccessor,
532: int numRows, int numCols) {
533: // Set pixel and line strides.
534: int dstPixelStride = dstAccessor.getPixelStride();
535: int dstScanlineStride = dstAccessor.getScanlineStride();
536: int magPixelStride = magAccessor.getPixelStride();
537: int magScanlineStride = magAccessor.getScanlineStride();
538: int phsPixelStride = phsAccessor.getPixelStride();
539: int phsScanlineStride = phsAccessor.getScanlineStride();
540:
541: // Loop over the destination components.
542: int numComponents = sampleModel.getNumBands() / 2;
543: for (int component = 0; component < numComponents; component++) {
544: // Set source band indices.
545: int dstBandReal = 2 * component;
546: int dstBandImag = dstBandReal + 1;
547:
548: // Get the source and destination arrays for this band.
549: byte[] dstReal = dstAccessor.getByteDataArray(dstBandReal);
550: byte[] dstImag = dstAccessor.getByteDataArray(dstBandImag);
551: byte[] magData = magAccessor.getByteDataArray(component);
552: byte[] phsData = phsAccessor.getByteDataArray(component);
553:
554: // Initialize the data offsets for this band.
555: int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal);
556: int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag);
557: int magOffset = magAccessor.getBandOffset(component);
558: int phsOffset = phsAccessor.getBandOffset(component);
559:
560: // Initialize the line offsets for looping.
561: int dstLineReal = dstOffsetReal;
562: int dstLineImag = dstOffsetImag;
563: int magLine = magOffset;
564: int phsLine = phsOffset;
565:
566: for (int row = 0; row < numRows; row++) {
567: // Initialize pixel offsets for this row.
568: int dstPixelReal = dstLineReal;
569: int dstPixelImag = dstLineImag;
570: int magPixel = magLine;
571: int phsPixel = phsLine;
572:
573: for (int col = 0; col < numCols; col++) {
574: double mag = magData[magPixel] & 0xff;
575: double phs = (phsData[phsPixel] & 0xff) * phaseGain
576: + phaseBias;
577:
578: dstReal[dstPixelReal] = ImageUtil
579: .clampRoundByte(mag * Math.cos(phs));
580: dstImag[dstPixelImag] = ImageUtil
581: .clampRoundByte(mag * Math.sin(phs));
582:
583: dstPixelReal += dstPixelStride;
584: dstPixelImag += dstPixelStride;
585: magPixel += magPixelStride;
586: phsPixel += phsPixelStride;
587: }
588:
589: // Increment the line offsets.
590: dstLineReal += dstScanlineStride;
591: dstLineImag += dstScanlineStride;
592: magLine += magScanlineStride;
593: phsLine += phsScanlineStride;
594: }
595: }
596: }
597: }
|