001: /*
002: * $RCSfile: DCTOpImage.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:22 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.geom.Point2D;
016: import java.awt.image.ColorModel;
017: import java.awt.image.DataBuffer;
018: import java.awt.image.Raster;
019: import java.awt.image.RenderedImage;
020: import java.awt.image.SampleModel;
021: import java.awt.image.WritableRaster;
022: import java.util.Arrays;
023: import java.util.Map;
024: import javax.media.jai.ImageLayout;
025: import javax.media.jai.RasterAccessor;
026: import javax.media.jai.RasterFormatTag;
027: import javax.media.jai.RasterFactory;
028: import javax.media.jai.UntiledOpImage;
029: import com.sun.media.jai.util.JDKWorkarounds;
030: import com.sun.media.jai.util.MathJAI;
031:
032: /**
033: * An <code>OpImage</code> implementing the forward and inverse even
034: * discrete cosine transform (DCT) operations as described in
035: * <code>javax.media.jai.operator.DCTDescriptor</code> and
036: * <code>javax.media.jai.operator.IDCTDescriptor</code>.
037: *
038: * <p> The DCT operation is implemented using a one-dimensional fast cosine
039: * transform (FCT) which is applied successively to the rows and the columns
040: * of the image. All image dimensions are enlarged to the next positive power
041: * of 2 greater than or equal to the respective dimension unless the dimension
042: * is unity in which case it is not modified. Source image values are padded
043: * with zeros when the dimension is smaller than the output power-of-2
044: * dimension.
045: *
046: * @since EA3
047: *
048: * @see javax.media.jai.UntiledOpImage
049: * @see javax.media.jai.operator.DCTDescriptor
050: * @see javax.media.jai.operator.IDCTDescriptor
051: *
052: */
053: public class DCTOpImage extends UntiledOpImage {
054: /**
055: * The Fast Cosine Transform object.
056: */
057: private FCT fct;
058:
059: /**
060: * Override the dimension specification for the destination such that it
061: * has width and height which are equal to non-negative powers of 2.
062: */
063: private static ImageLayout layoutHelper(ImageLayout layout,
064: RenderedImage source) {
065: // Create an ImageLayout or clone the one passed in.
066: ImageLayout il = layout == null ? new ImageLayout()
067: : (ImageLayout) layout.clone();
068:
069: // Force the origin to coincide with that of the source.
070: il.setMinX(source.getMinX());
071: il.setMinY(source.getMinY());
072:
073: // Recalculate the non-unity dimensions to be a positive power of 2.
074: // XXX This calculation should not be effected if an implementation
075: // of the FCT which supports arbitrary dimensions is used.
076: boolean createNewSampleModel = false;
077: int w = il.getWidth(source);
078: if (w > 1) {
079: int newWidth = MathJAI.nextPositivePowerOf2(w);
080: if (newWidth != w) {
081: il.setWidth(w = newWidth);
082: createNewSampleModel = true;
083: }
084: }
085: int h = il.getHeight(source);
086: if (h > 1) {
087: int newHeight = MathJAI.nextPositivePowerOf2(h);
088: if (newHeight != h) {
089: il.setHeight(h = newHeight);
090: createNewSampleModel = true;
091: }
092: }
093:
094: // Force the image to contain floating point data.
095: SampleModel sm = il.getSampleModel(source);
096: int dataType = sm.getTransferType();
097: if (dataType != DataBuffer.TYPE_FLOAT
098: && dataType != DataBuffer.TYPE_DOUBLE) {
099: dataType = DataBuffer.TYPE_FLOAT;
100: createNewSampleModel = true;
101: }
102:
103: // Create a new SampleModel for the destination.
104: if (createNewSampleModel) {
105: sm = RasterFactory.createComponentSampleModel(sm, dataType,
106: w, h, sm.getNumBands());
107: il.setSampleModel(sm);
108:
109: // Clear the ColorModel mask if needed.
110: ColorModel cm = il.getColorModel(null);
111: if (cm != null
112: && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
113: // Clear the mask bit if incompatible.
114: il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
115: }
116: }
117:
118: return il;
119: }
120:
121: /**
122: * Constructs a <code>DCTOpImage</code> object.
123: *
124: * <p>The image dimensions are the respective next positive powers of 2
125: * greater than or equal to the dimensions of the source image. The tile
126: * grid layout, SampleModel, and ColorModel may optionally be specified
127: * by an ImageLayout object.
128: *
129: * @param source A RenderedImage.
130: * @param layout An ImageLayout optionally containing the tile grid layout,
131: * SampleModel, and ColorModel, or null.
132: * @param fct The Fast Cosine Transform object.
133: */
134: public DCTOpImage(RenderedImage source, Map config,
135: ImageLayout layout, FCT fct) {
136: super (source, config, layoutHelper(layout, source));
137:
138: // Cache the FCT object.
139: this .fct = fct;
140: }
141:
142: /**
143: * Computes the source point corresponding to the supplied point.
144: *
145: * @param destPt the position in destination image coordinates
146: * to map to source image coordinates.
147: *
148: * @return <code>null</code>.
149: *
150: * @throws IllegalArgumentException if <code>destPt</code> is
151: * <code>null</code>.
152: *
153: * @since JAI 1.1.2
154: */
155: public Point2D mapDestPoint(Point2D destPt) {
156: if (destPt == null) {
157: throw new IllegalArgumentException(JaiI18N
158: .getString("Generic0"));
159: }
160:
161: return null;
162: }
163:
164: /**
165: * Computes the destination point corresponding to the supplied point.
166: *
167: * @return <code>null</code>.
168: *
169: * @throws IllegalArgumentException if <code>sourcePt</code> is
170: * <code>null</code>.
171: *
172: * @since JAI 1.1.2
173: */
174: public Point2D mapSourcePoint(Point2D sourcePt) {
175: if (sourcePt == null) {
176: throw new IllegalArgumentException(JaiI18N
177: .getString("Generic0"));
178: }
179:
180: return null;
181: }
182:
183: /*
184: * Calculate the discrete cosine transform of the source image.
185: *
186: * @param source The source Raster; should be the whole image.
187: * @param dest The destination WritableRaster; should be the whole image.
188: * @param destRect The destination Rectangle; should be the image bounds.
189: */
190: protected void computeImage(Raster[] sources, WritableRaster dest,
191: Rectangle destRect) {
192: Raster source = sources[0];
193:
194: // Degenerate case.
195: if (destRect.width == 1 && destRect.height == 1) {
196: double[] pixel = source.getPixel(destRect.x, destRect.y,
197: (double[]) null);
198: dest.setPixel(destRect.x, destRect.y, pixel);
199: return;
200: }
201:
202: // Initialize to first non-unity length to be encountered.
203: fct.setLength(destRect.width > 1 ? getWidth() : getHeight());
204:
205: // Get some information about the source image.
206: int srcWidth = source.getWidth();
207: int srcHeight = source.getHeight();
208: int srcX = source.getMinX();
209: int srcY = source.getMinY();
210:
211: // Retrieve format tags.
212: RasterFormatTag[] formatTags = getFormatTags();
213:
214: RasterAccessor srcAccessor = new RasterAccessor(source,
215: new Rectangle(srcX, srcY, srcWidth, srcHeight),
216: formatTags[0], getSourceImage(0).getColorModel());
217: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
218: formatTags[1], getColorModel());
219:
220: // Set data type flags.
221: int srcDataType = srcAccessor.getDataType();
222: int dstDataType = dstAccessor.getDataType();
223:
224: // Set pixel and line strides.
225: int srcPixelStride = srcAccessor.getPixelStride();
226: int srcScanlineStride = srcAccessor.getScanlineStride();
227: int dstPixelStride = dstAccessor.getPixelStride();
228: int dstScanlineStride = dstAccessor.getScanlineStride();
229:
230: // Loop over the bands.
231: int numBands = sampleModel.getNumBands();
232: for (int band = 0; band < numBands; band++) {
233: // Get the source and destination arrays for this band.
234: Object srcData = srcAccessor.getDataArray(band);
235: Object dstData = dstAccessor.getDataArray(band);
236:
237: if (destRect.width > 1) {
238: // Set the FCT length.
239: fct.setLength(getWidth());
240:
241: // Initialize the data offsets for this band.
242: int srcOffset = srcAccessor.getBandOffset(band);
243: int dstOffset = dstAccessor.getBandOffset(band);
244:
245: // Perform the row transforms.
246: for (int row = 0; row < srcHeight; row++) {
247: // Set the input data of the FCT.
248: fct.setData(srcDataType, srcData, srcOffset,
249: srcPixelStride, srcWidth);
250:
251: // Calculate the DFT of the row.
252: fct.transform();
253:
254: // Get the output data of the FCT.
255: fct.getData(dstDataType, dstData, dstOffset,
256: dstPixelStride);
257:
258: // Increment the data offsets.
259: srcOffset += srcScanlineStride;
260: dstOffset += dstScanlineStride;
261: }
262: }
263:
264: if (destRect.width == 1) { // destRect.height > 1
265: // Initialize the data offsets for this band.
266: int srcOffset = srcAccessor.getBandOffset(band);
267: int dstOffset = dstAccessor.getBandOffset(band);
268:
269: // Set the input data of the FCT.
270: fct.setData(srcDataType, srcData, srcOffset,
271: srcScanlineStride, srcHeight);
272:
273: // Calculate the DFT of the row.
274: fct.transform();
275:
276: // Get the output data of the FCT.
277: fct.getData(dstDataType, dstData, dstOffset,
278: dstScanlineStride);
279: } else if (destRect.height > 1) { // destRect.width > 1
280: // Reset the FCT length.
281: fct.setLength(getHeight());
282:
283: // Initialize destination offset.
284: int dstOffset = dstAccessor.getBandOffset(band);
285:
286: // Perform the column transforms.
287: for (int col = 0; col < destRect.width; col++) {
288: // Set the input data of the FCT.
289: fct.setData(dstDataType, dstData, dstOffset,
290: dstScanlineStride, destRect.height);
291:
292: // Calculate the DFT of the column.
293: fct.transform();
294:
295: // Get the output data of the FCT.
296: fct.getData(dstDataType, dstData, dstOffset,
297: dstScanlineStride);
298:
299: // Increment the data offset.
300: dstOffset += dstPixelStride;
301: }
302: }
303: }
304:
305: if (dstAccessor.needsClamping()) {
306: dstAccessor.clampDataArrays();
307: }
308:
309: // Make sure that the output data is copied to the destination.
310: dstAccessor.copyDataToRaster();
311: }
312: }
|