001: /*
002: * $RCSfile: RescaleOpImage.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.Raster;
017: import java.awt.image.RenderedImage;
018: import java.awt.image.WritableRaster;
019: import javax.media.jai.ColormapOpImage;
020: import javax.media.jai.ImageLayout;
021: import javax.media.jai.PointOpImage;
022: import javax.media.jai.RasterAccessor;
023: import javax.media.jai.RasterFormatTag;
024: import java.util.Map;
025: import com.sun.media.jai.util.ImageUtil;
026:
027: /**
028: * An <code>OpImage</code> implementing the "Rescale" operation.
029: *
030: * <p> The "Rescale" operation maps the pixel values of an image from
031: * one range to another range by multiplying each pixel value by one
032: * of a set of constants and then adding another constant to the
033: * result of the multiplication. The pixel values of the destination
034: * image are defined by the pseudocode:
035: *
036: * <pre>
037: * for (int h = 0; h < dstHeight; h++) {
038: * for (int w = 0; w < dstWidth; w++) {
039: * for (int b = 0; b < dstNumBands; b++) {
040: * scale = (scales.length < dstNumBands)?
041: * scales[0]:scales[b];
042: * offset = (offsets.length < dstNumBands)?
043: * offsets[0]:offsets[b];
044: * dst[h][w][b] = srcs[h][w][b] * scale + offset;
045: * }
046: * }
047: * }
048: * </pre>
049: *
050: * @see javax.media.jai.operator.RescaleDescriptor
051: * @see RescaleCRIF
052: *
053: *
054: * @since EA3
055: */
056: final class RescaleOpImage extends ColormapOpImage {
057:
058: /** The constants to be multiplied, one for each band. */
059: protected double[] constants;
060: protected double[] offsets;
061:
062: private byte[][] byteTable = null;
063:
064: private synchronized void initByteTable() {
065:
066: if (byteTable != null) {
067: return;
068: }
069:
070: int nbands = constants.length;
071:
072: byteTable = new byte[nbands][256];
073:
074: // Initialize table which implements Rescale and clamp
075: for (int band = 0; band < nbands; band++) {
076: byte[] t = byteTable[band];
077: double c = constants[band];
078: double o = offsets[band];
079: for (int i = 0; i < 256; i++) {
080: t[i] = ImageUtil.clampRoundByte(i * c + o);
081: }
082: }
083: }
084:
085: /**
086: * Constructor.
087: *
088: * @param source The source image.
089: * @param configuration Configurable attributes of the image including
090: * configuration variables indexed by
091: * <code>RenderingHints.Key</code>s and image properties indexed
092: * by <code>String</code>s or <code>CaselessStringKey</code>s.
093: * This is simply forwarded to the superclass constructor.
094: * @param layout The destination image layout.
095: * @param constants The constants to be multiplied, stored as reference.
096: * @param offsets The offsets to be added, stored as reference.
097: */
098: public RescaleOpImage(RenderedImage source, Map config,
099: ImageLayout layout, double[] constants, double[] offsets) {
100: super (source, layout, config, true);
101:
102: int numBands = getSampleModel().getNumBands();
103:
104: if (constants.length < numBands) {
105: this .constants = new double[numBands];
106: for (int i = 0; i < numBands; i++) {
107: this .constants[i] = constants[0];
108: }
109: } else {
110: this .constants = constants;
111: }
112:
113: if (offsets.length < numBands) {
114: this .offsets = new double[numBands];
115: for (int i = 0; i < numBands; i++) {
116: this .offsets[i] = offsets[0];
117: }
118: } else {
119: this .offsets = offsets;
120: }
121:
122: // Set flag to permit in-place operation.
123: permitInPlaceOperation();
124:
125: // Initialize the colormap if necessary.
126: initializeColormapOperation();
127: }
128:
129: /**
130: * Transform the colormap according to the rescaling parameters.
131: */
132: protected void transformColormap(byte[][] colormap) {
133: for (int b = 0; b < 3; b++) {
134: byte[] map = colormap[b];
135: int mapSize = map.length;
136:
137: float c = (float) (b < constants.length ? constants[b]
138: : constants[0]);
139: float o = (float) (b < constants.length ? offsets[b]
140: : offsets[0]);
141:
142: for (int i = 0; i < mapSize; i++) {
143: map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) * c
144: + o);
145: }
146: }
147: }
148:
149: /**
150: * Rescales to the pixel values within a specified rectangle.
151: *
152: * @param sources Cobbled sources, guaranteed to provide all the
153: * source data necessary for computing the rectangle.
154: * @param dest The tile containing the rectangle to be computed.
155: * @param destRect The rectangle within the tile to be computed.
156: */
157: protected void computeRect(Raster[] sources, WritableRaster dest,
158: Rectangle destRect) {
159: // Retrieve format tags.
160: RasterFormatTag[] formatTags = getFormatTags();
161:
162: Rectangle srcRect = mapDestRect(destRect, 0);
163:
164: RasterAccessor dst = new RasterAccessor(dest, destRect,
165: formatTags[1], getColorModel());
166: RasterAccessor src = new RasterAccessor(sources[0], srcRect,
167: formatTags[0], getSource(0).getColorModel());
168:
169: switch (dst.getDataType()) {
170: case DataBuffer.TYPE_BYTE:
171: computeRectByte(src, dst);
172: break;
173: case DataBuffer.TYPE_USHORT:
174: computeRectUShort(src, dst);
175: break;
176: case DataBuffer.TYPE_SHORT:
177: computeRectShort(src, dst);
178: break;
179: case DataBuffer.TYPE_INT:
180: computeRectInt(src, dst);
181: break;
182: case DataBuffer.TYPE_FLOAT:
183: computeRectFloat(src, dst);
184: break;
185: case DataBuffer.TYPE_DOUBLE:
186: computeRectDouble(src, dst);
187: break;
188: }
189:
190: if (dst.needsClamping()) {
191: /* Further clamp down to underlying raster data type. */
192: dst.clampDataArrays();
193: }
194: dst.copyDataToRaster();
195: }
196:
197: private void computeRectByte(RasterAccessor src, RasterAccessor dst) {
198: int dstWidth = dst.getWidth();
199: int dstHeight = dst.getHeight();
200: int dstBands = dst.getNumBands();
201:
202: int dstLineStride = dst.getScanlineStride();
203: int dstPixelStride = dst.getPixelStride();
204: int[] dstBandOffsets = dst.getBandOffsets();
205: byte[][] dstData = dst.getByteDataArrays();
206:
207: int srcLineStride = src.getScanlineStride();
208: int srcPixelStride = src.getPixelStride();
209: int[] srcBandOffsets = src.getBandOffsets();
210: byte[][] srcData = src.getByteDataArrays();
211:
212: initByteTable();
213:
214: for (int b = 0; b < dstBands; b++) {
215: byte[] d = dstData[b];
216: byte[] s = srcData[b];
217:
218: int dstLineOffset = dstBandOffsets[b];
219: int srcLineOffset = srcBandOffsets[b];
220:
221: byte[] clamp = byteTable[b];
222: double c = constants[b];
223: double o = offsets[b];
224:
225: for (int h = 0; h < dstHeight; h++) {
226: int dstPixelOffset = dstLineOffset;
227: int srcPixelOffset = srcLineOffset;
228:
229: dstLineOffset += dstLineStride;
230: srcLineOffset += srcLineStride;
231:
232: for (int w = 0; w < dstWidth; w++) {
233: d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF];
234:
235: dstPixelOffset += dstPixelStride;
236: srcPixelOffset += srcPixelStride;
237: }
238: }
239: }
240: }
241:
242: private void computeRectUShort(RasterAccessor src,
243: RasterAccessor dst) {
244: int dstWidth = dst.getWidth();
245: int dstHeight = dst.getHeight();
246: int dstBands = dst.getNumBands();
247:
248: int dstLineStride = dst.getScanlineStride();
249: int dstPixelStride = dst.getPixelStride();
250: int[] dstBandOffsets = dst.getBandOffsets();
251: short[][] dstData = dst.getShortDataArrays();
252:
253: int srcLineStride = src.getScanlineStride();
254: int srcPixelStride = src.getPixelStride();
255: int[] srcBandOffsets = src.getBandOffsets();
256: short[][] srcData = src.getShortDataArrays();
257:
258: for (int b = 0; b < dstBands; b++) {
259: float c = (float) constants[b];
260: float o = (float) offsets[b];
261: short[] d = dstData[b];
262: short[] s = srcData[b];
263:
264: int dstLineOffset = dstBandOffsets[b];
265: int srcLineOffset = srcBandOffsets[b];
266:
267: for (int h = 0; h < dstHeight; h++) {
268: int dstPixelOffset = dstLineOffset;
269: int srcPixelOffset = srcLineOffset;
270:
271: dstLineOffset += dstLineStride;
272: srcLineOffset += srcLineStride;
273:
274: for (int w = 0; w < dstWidth; w++) {
275: d[dstPixelOffset] = ImageUtil
276: .clampRoundUShort((s[srcPixelOffset] & 0xFFFF)
277: * c + o);
278:
279: dstPixelOffset += dstPixelStride;
280: srcPixelOffset += srcPixelStride;
281: }
282: }
283: }
284: }
285:
286: private void computeRectShort(RasterAccessor src, RasterAccessor dst) {
287: int dstWidth = dst.getWidth();
288: int dstHeight = dst.getHeight();
289: int dstBands = dst.getNumBands();
290:
291: int dstLineStride = dst.getScanlineStride();
292: int dstPixelStride = dst.getPixelStride();
293: int[] dstBandOffsets = dst.getBandOffsets();
294: short[][] dstData = dst.getShortDataArrays();
295:
296: int srcLineStride = src.getScanlineStride();
297: int srcPixelStride = src.getPixelStride();
298: int[] srcBandOffsets = src.getBandOffsets();
299: short[][] srcData = src.getShortDataArrays();
300:
301: for (int b = 0; b < dstBands; b++) {
302: float c = (float) constants[b];
303: float o = (float) offsets[b];
304: short[] d = dstData[b];
305: short[] s = srcData[b];
306:
307: int dstLineOffset = dstBandOffsets[b];
308: int srcLineOffset = srcBandOffsets[b];
309:
310: for (int h = 0; h < dstHeight; h++) {
311: int dstPixelOffset = dstLineOffset;
312: int srcPixelOffset = srcLineOffset;
313:
314: dstLineOffset += dstLineStride;
315: srcLineOffset += srcLineStride;
316:
317: for (int w = 0; w < dstWidth; w++) {
318: d[dstPixelOffset] = ImageUtil
319: .clampRoundShort(s[srcPixelOffset] * c + o);
320:
321: dstPixelOffset += dstPixelStride;
322: srcPixelOffset += srcPixelStride;
323: }
324: }
325: }
326: }
327:
328: private void computeRectInt(RasterAccessor src, RasterAccessor dst) {
329: int dstWidth = dst.getWidth();
330: int dstHeight = dst.getHeight();
331: int dstBands = dst.getNumBands();
332:
333: int dstLineStride = dst.getScanlineStride();
334: int dstPixelStride = dst.getPixelStride();
335: int[] dstBandOffsets = dst.getBandOffsets();
336: int[][] dstData = dst.getIntDataArrays();
337:
338: int srcLineStride = src.getScanlineStride();
339: int srcPixelStride = src.getPixelStride();
340: int[] srcBandOffsets = src.getBandOffsets();
341: int[][] srcData = src.getIntDataArrays();
342:
343: for (int b = 0; b < dstBands; b++) {
344: double c = constants[b];
345: double o = offsets[b];
346: int[] d = dstData[b];
347: int[] s = srcData[b];
348:
349: int dstLineOffset = dstBandOffsets[b];
350: int srcLineOffset = srcBandOffsets[b];
351:
352: for (int h = 0; h < dstHeight; h++) {
353: int dstPixelOffset = dstLineOffset;
354: int srcPixelOffset = srcLineOffset;
355:
356: dstLineOffset += dstLineStride;
357: srcLineOffset += srcLineStride;
358:
359: for (int w = 0; w < dstWidth; w++) {
360: d[dstPixelOffset] = ImageUtil
361: .clampRoundInt(s[srcPixelOffset] * c + o);
362:
363: dstPixelOffset += dstPixelStride;
364: srcPixelOffset += srcPixelStride;
365: }
366: }
367: }
368: }
369:
370: private void computeRectFloat(RasterAccessor src, RasterAccessor dst) {
371: int dstWidth = dst.getWidth();
372: int dstHeight = dst.getHeight();
373: int dstBands = dst.getNumBands();
374:
375: int dstLineStride = dst.getScanlineStride();
376: int dstPixelStride = dst.getPixelStride();
377: int[] dstBandOffsets = dst.getBandOffsets();
378: float[][] dstData = dst.getFloatDataArrays();
379:
380: int srcLineStride = src.getScanlineStride();
381: int srcPixelStride = src.getPixelStride();
382: int[] srcBandOffsets = src.getBandOffsets();
383: float[][] srcData = src.getFloatDataArrays();
384:
385: for (int b = 0; b < dstBands; b++) {
386: double c = constants[b];
387: double o = offsets[b];
388: float[] d = dstData[b];
389: float[] s = srcData[b];
390:
391: int dstLineOffset = dstBandOffsets[b];
392: int srcLineOffset = srcBandOffsets[b];
393:
394: for (int h = 0; h < dstHeight; h++) {
395: int dstPixelOffset = dstLineOffset;
396: int srcPixelOffset = srcLineOffset;
397:
398: dstLineOffset += dstLineStride;
399: srcLineOffset += srcLineStride;
400:
401: for (int w = 0; w < dstWidth; w++) {
402: d[dstPixelOffset] = ImageUtil
403: .clampFloat(s[srcPixelOffset] * c + o);
404:
405: dstPixelOffset += dstPixelStride;
406: srcPixelOffset += srcPixelStride;
407: }
408: }
409: }
410: }
411:
412: private void computeRectDouble(RasterAccessor src,
413: RasterAccessor dst) {
414: int dstWidth = dst.getWidth();
415: int dstHeight = dst.getHeight();
416: int dstBands = dst.getNumBands();
417:
418: int dstLineStride = dst.getScanlineStride();
419: int dstPixelStride = dst.getPixelStride();
420: int[] dstBandOffsets = dst.getBandOffsets();
421: double[][] dstData = dst.getDoubleDataArrays();
422:
423: int srcLineStride = src.getScanlineStride();
424: int srcPixelStride = src.getPixelStride();
425: int[] srcBandOffsets = src.getBandOffsets();
426: double[][] srcData = src.getDoubleDataArrays();
427:
428: for (int b = 0; b < dstBands; b++) {
429: double c = constants[b];
430: double o = offsets[b];
431: double[] d = dstData[b];
432: double[] s = srcData[b];
433:
434: int dstLineOffset = dstBandOffsets[b];
435: int srcLineOffset = srcBandOffsets[b];
436:
437: for (int h = 0; h < dstHeight; h++) {
438: int dstPixelOffset = dstLineOffset;
439: int srcPixelOffset = srcLineOffset;
440:
441: dstLineOffset += dstLineStride;
442: srcLineOffset += srcLineStride;
443:
444: for (int w = 0; w < dstWidth; w++) {
445: d[dstPixelOffset] = s[srcPixelOffset] * c + o;
446:
447: dstPixelOffset += dstPixelStride;
448: srcPixelOffset += srcPixelStride;
449: }
450: }
451: }
452: }
453: }
|