1 /*
2  * $RCSfile: PiecewiseOpImage.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:56:40 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.media.jai.opimage;
13 
14 import com.lightcrafts.mediax.jai.ColormapOpImage;
15 import java.awt.Rectangle;
16 import java.awt.image.DataBuffer;
17 import java.awt.image.Raster;
18 import java.awt.image.RenderedImage;
19 import java.awt.image.WritableRaster;
20 import com.lightcrafts.mediax.jai.ImageLayout;
21 import com.lightcrafts.mediax.jai.LookupTableJAI;
22 import com.lightcrafts.mediax.jai.RasterAccessor;
23 import com.lightcrafts.mediax.jai.RasterFormatTag;
24 import java.util.Map;
25 import com.lightcrafts.media.jai.util.ImageUtil;
26 
27 /**
28  * An <code>OpImage</code> implementing the "Piecewise" operation.
29  *
30  * <p> The "Piecewise" operation maps the pixel values of an image using
31  * a piecewise linear function represented by a set of breakpoints for each
32  * band. The abscissa of each breakpoint is the source image gray level for
33  * the band in question and the ordinate is the destination image gray level
34  * to which it is mapped.
35  *
36  * @see com.lightcrafts.mediax.jai.operator.PiecewiseDescriptor
37  * @see PiecewiseCRIF
38  *
39  *
40  * @since EA4
41  */
42 final class PiecewiseOpImage extends ColormapOpImage {
43     /** The abscissas of the breakpoints. */
44     private float[][] abscissas;
45 
46     /** The slope at each abscissa. */
47     private float[][] slopes;
48 
49     /** The intercept at each abscissa. */
50     private float[][] intercepts;
51 
52     /** The minimum values of the output per band. */
53     private float[] minOrdinates;
54 
55     /** The maximum values of the output per band. */
56     private float[] maxOrdinates;
57 
58     /** Flag indicating byte data. */
59     private boolean isByteData = false;
60 
61     /** A lookup table for use in the case of byte data. */
62     private LookupTableJAI lut;
63 
64     /**
65      * Find the ordinate value for a given abscissa value.
66      *
67      * @param x The abscissa array.
68      * @param minValue The minimum source gray level in the breakpoint set.
69      * @param maxValue The maximum source gray level in the breakpoint set.
70      * @param a The array of piecewise slopes.
71      * @param b The array of piecewise ordinate intercepts.
72      * @param value The source gray level.
73      * @return The destination gray level.
74      */
binarySearch(float[] x, float minValue, float maxValue, float[] a, float[] b, float value)75     private static float binarySearch(float[] x,
76                                       float minValue, float maxValue,
77                                       float[] a, float[] b,
78                                       float value) {
79         int highIndex = x.length - 1;
80 
81         if(value <= x[0]) {
82             return minValue;
83         } else if(value >= x[highIndex]) {
84             return maxValue;
85         }
86 
87         int lowIndex = 0;
88         int deltaIndex = highIndex - lowIndex;
89 
90         while(deltaIndex > 1) {
91             int meanIndex = lowIndex + deltaIndex/2;
92             if(value >= x[meanIndex]) {
93                 lowIndex = meanIndex;
94             } else {
95                 highIndex = meanIndex;
96             }
97             deltaIndex = highIndex - lowIndex;
98         }
99 
100         return a[lowIndex]*value + b[lowIndex];
101     }
102 
103     /**
104      * Constructor.
105      *
106      * @param source       The source image.
107      * @param layout       The destination image layout.
108      * @param breakpoints  The piecewise mapping stored by reference. The
109      * arrays breakpoints[b][0] and breakpoints[b][1] represent the abscissas
110      * and ordinates of the breakpoints, respectively, for band <i>b</i>. The
111      * number of sets of breakpoints must be one or equal to the number of
112      * image bands.
113      */
PiecewiseOpImage(RenderedImage source, Map config, ImageLayout layout, float[][][] breakpoints)114     public PiecewiseOpImage(RenderedImage source,
115                             Map config,
116                             ImageLayout layout,
117                             float[][][] breakpoints) {
118         super(source, layout, config, true);
119 
120         // Ensure that the number of sets of breakpoints is either unity
121         // or equal to the number of bands.
122         int numBands = sampleModel.getNumBands();
123 
124         // Initalize the instance variables.
125         initFields(numBands, breakpoints);
126 
127         // Set the byte data flag.
128         isByteData = sampleModel.getTransferType() == DataBuffer.TYPE_BYTE;
129 
130         // Perform byte-specific initialization.
131         if(isByteData) {
132             // Initialize the lookup table.
133             createLUT();
134 
135             // Clear the other instance variables for the garbage collector.
136             unsetFields();
137         }
138 
139         // Set flag to permit in-place operation.
140         permitInPlaceOperation();
141 
142         // Initialize the colormap if necessary.
143         initializeColormapOperation();
144     }
145 
146     /**
147      * Transform the colormap according to the rescaling parameters.
148      */
transformColormap(byte[][] colormap)149     protected void transformColormap(byte[][] colormap) {
150 
151 	byte byteTable[][] = lut.getByteData();
152 
153         for(int b = 0; b < 3; b++) {
154             byte[] map = colormap[b];
155 	    byte[] luTable = byteTable[b >= byteTable.length ? 0 : b];
156             int mapSize = map.length;
157 
158             for(int i = 0; i < mapSize; i++) {
159                 map[i] = luTable[(map[i] & 0xFF)];
160             }
161         }
162     }
163 
164     /**
165      * Initialize various instance variables from the array of breakpoints.
166      * The principal derived values are the slope and ordinate-intercept of
167      * each piecewise segment.
168      *
169      * @param numBands The number of bands in the image.
170      * @param The breakpoints as breakpoints[numBands][0..1][numPoints].
171      */
initFields(int numBands, float[][][] breakpoints)172     private void initFields(int numBands, float[][][] breakpoints) {
173         abscissas = new float[numBands][];
174         slopes = new float[numBands][];
175         intercepts = new float[numBands][];
176         minOrdinates = new float[numBands];
177         maxOrdinates = new float[numBands];
178 
179         for(int band = 0; band < numBands; band++) {
180             abscissas[band] = breakpoints.length == 1 ?
181                 breakpoints[0][0] : breakpoints[band][0];
182             int maxIndex = abscissas[band].length - 1;
183 
184             minOrdinates[band] = breakpoints.length == 1 ?
185                 breakpoints[0][1][0] : breakpoints[band][1][0];
186             maxOrdinates[band] = breakpoints.length == 1 ?
187                 breakpoints[0][1][maxIndex] : breakpoints[band][1][maxIndex];
188 
189             slopes[band] = new float[maxIndex];
190             intercepts[band] = new float[maxIndex];
191 
192             float[] x =  abscissas[band];
193             float[] y =  breakpoints.length == 1 ?
194                 breakpoints[0][1] : breakpoints[band][1];
195             float[] a = slopes[band];
196             float[] b = intercepts[band];
197             for(int i1 = 0; i1 < maxIndex; i1++) {
198                 int i2 = i1 + 1;
199                 a[i1] = (y[i2]-y[i1])/(x[i2] - x[i1]);
200                 b[i1] = y[i1] - x[i1]*a[i1];
201             }
202         }
203     }
204 
205 
206     /**
207      * Clear all instance fields which are ununsed references so the GC
208      * may clear them.
209      */
unsetFields()210     private void unsetFields() {
211         abscissas = null;
212         slopes = null;
213         intercepts = null;
214         minOrdinates = null;
215         maxOrdinates = null;
216     }
217 
218     /**
219      * Create a lookup table to be used in the case of byte data.
220      */
createLUT()221     private void createLUT() {
222         // Allocate memory for the data array references.
223         int numBands = abscissas.length;
224         byte[][] data = new byte[numBands][];
225 
226         // Generate the data for each band.
227         for(int band = 0; band < numBands; band++) {
228             // Allocate memory for this band.
229             data[band] = new byte[256];
230 
231             // Cache the references to avoid extra indexing.
232             byte[] table = data[band];
233             float[] x = abscissas[band];
234             float[] a = slopes[band];
235             float[] b = intercepts[band];
236             float yL = minOrdinates[band];
237             float yH = maxOrdinates[band];
238 
239             // Initialize the lookup table data.
240             for(int value = 0; value < 256; value++) {
241                 table[value] =
242                     ImageUtil.clampRoundByte(binarySearch(x, yL, yH, a, b, value));
243             }
244         }
245 
246         // Construct the lookup table.
247         lut = new LookupTableJAI(data);
248     }
249 
250     /**
251      * Piecewises to the pixel values within a specified rectangle.
252      *
253      * @param sources   Cobbled sources, guaranteed to provide all the
254      *                  source data necessary for computing the rectangle.
255      * @param dest      The tile containing the rectangle to be computed.
256      * @param destRect  The rectangle within the tile to be computed.
257      */
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)258     protected void computeRect(Raster[] sources,
259                                WritableRaster dest,
260                                Rectangle destRect) {
261         // Retrieve format tags.
262         RasterFormatTag[] formatTags = getFormatTags();
263 
264         if(isByteData) {
265             computeRectByte(sources, dest, destRect);
266         } else {
267             RasterAccessor dst =
268                 new RasterAccessor(dest, destRect, formatTags[1],
269                                    getColorModel());
270             RasterAccessor src =
271                 new RasterAccessor(sources[0], destRect, formatTags[0],
272                                    getSource(0).getColorModel());
273 
274             switch (dst.getDataType()) {
275             case DataBuffer.TYPE_USHORT:
276                 computeRectUShort(src, dst);
277                 break;
278             case DataBuffer.TYPE_SHORT:
279                 computeRectShort(src, dst);
280                 break;
281             case DataBuffer.TYPE_INT:
282                 computeRectInt(src, dst);
283                 break;
284             case DataBuffer.TYPE_FLOAT:
285                 computeRectFloat(src, dst);
286                 break;
287             case DataBuffer.TYPE_DOUBLE:
288                 computeRectDouble(src, dst);
289                 break;
290             }
291 
292             dst.copyDataToRaster();
293         }
294     }
295 
computeRectByte(Raster[] sources, WritableRaster dest, Rectangle destRect)296     private void computeRectByte(Raster[] sources,
297                                  WritableRaster dest,
298                                  Rectangle destRect) {
299         lut.lookup(sources[0], dest, destRect);
300     }
301 
computeRectUShort(RasterAccessor src, RasterAccessor dst)302     private void computeRectUShort(RasterAccessor src,
303                                    RasterAccessor dst) {
304         int dstWidth = dst.getWidth();
305         int dstHeight = dst.getHeight();
306         int dstBands = dst.getNumBands();
307 
308         int dstLineStride = dst.getScanlineStride();
309         int dstPixelStride = dst.getPixelStride();
310         int[] dstBandOffsets = dst.getBandOffsets();
311         short[][] dstData = dst.getShortDataArrays();
312 
313         int srcLineStride = src.getScanlineStride();
314         int srcPixelStride = src.getPixelStride();
315         int[] srcBandOffsets = src.getBandOffsets();
316         short[][] srcData = src.getShortDataArrays();
317 
318         for (int b = 0; b < dstBands; b++) {
319             short[] d = dstData[b];
320             short[] s = srcData[b];
321 
322             int dstLineOffset = dstBandOffsets[b];
323             int srcLineOffset = srcBandOffsets[b];
324 
325             // Cache the references to avoid extra indexing.
326             float[] x = abscissas[b];
327             float[] gain = slopes[b];
328             float[] bias = intercepts[b];
329             float yL = minOrdinates[b];
330             float yH = maxOrdinates[b];
331 
332             for (int h = 0; h < dstHeight; h++) {
333                 int dstPixelOffset = dstLineOffset;
334                 int srcPixelOffset = srcLineOffset;
335 
336                 dstLineOffset += dstLineStride;
337                 srcLineOffset += srcLineStride;
338 
339                 for (int w = 0; w < dstWidth; w++) {
340                     d[dstPixelOffset] =
341                         ImageUtil.clampRoundUShort(binarySearch(x, yL, yH, gain, bias,
342                                                       s[srcPixelOffset] &
343                                                       0xFFFF));
344 
345                     dstPixelOffset += dstPixelStride;
346                     srcPixelOffset += srcPixelStride;
347                 }
348             }
349         }
350     }
351 
computeRectShort(RasterAccessor src, RasterAccessor dst)352     private void computeRectShort(RasterAccessor src,
353                                   RasterAccessor dst) {
354         int dstWidth = dst.getWidth();
355         int dstHeight = dst.getHeight();
356         int dstBands = dst.getNumBands();
357 
358         int dstLineStride = dst.getScanlineStride();
359         int dstPixelStride = dst.getPixelStride();
360         int[] dstBandOffsets = dst.getBandOffsets();
361         short[][] dstData = dst.getShortDataArrays();
362 
363         int srcLineStride = src.getScanlineStride();
364         int srcPixelStride = src.getPixelStride();
365         int[] srcBandOffsets = src.getBandOffsets();
366         short[][] srcData = src.getShortDataArrays();
367 
368         for (int b = 0; b < dstBands; b++) {
369             short[] d = dstData[b];
370             short[] s = srcData[b];
371 
372             int dstLineOffset = dstBandOffsets[b];
373             int srcLineOffset = srcBandOffsets[b];
374 
375             // Cache the references to avoid extra indexing.
376             float[] x = abscissas[b];
377             float[] gain = slopes[b];
378             float[] bias = intercepts[b];
379             float yL = minOrdinates[b];
380             float yH = maxOrdinates[b];
381 
382             for (int h = 0; h < dstHeight; h++) {
383                 int dstPixelOffset = dstLineOffset;
384                 int srcPixelOffset = srcLineOffset;
385 
386                 dstLineOffset += dstLineStride;
387                 srcLineOffset += srcLineStride;
388 
389                 for (int w = 0; w < dstWidth; w++) {
390                     d[dstPixelOffset] =
391                         ImageUtil.clampRoundShort(binarySearch(x, yL, yH, gain, bias,
392                                                      s[srcPixelOffset]));
393 
394                     dstPixelOffset += dstPixelStride;
395                     srcPixelOffset += srcPixelStride;
396                 }
397             }
398         }
399     }
400 
computeRectInt(RasterAccessor src, RasterAccessor dst)401     private void computeRectInt(RasterAccessor src,
402                                 RasterAccessor dst) {
403         int dstWidth = dst.getWidth();
404         int dstHeight = dst.getHeight();
405         int dstBands = dst.getNumBands();
406 
407         int dstLineStride = dst.getScanlineStride();
408         int dstPixelStride = dst.getPixelStride();
409         int[] dstBandOffsets = dst.getBandOffsets();
410         int[][] dstData = dst.getIntDataArrays();
411 
412         int srcLineStride = src.getScanlineStride();
413         int srcPixelStride = src.getPixelStride();
414         int[] srcBandOffsets = src.getBandOffsets();
415         int[][] srcData = src.getIntDataArrays();
416 
417         for (int b = 0; b < dstBands; b++) {
418             int[] d = dstData[b];
419             int[] s = srcData[b];
420 
421             int dstLineOffset = dstBandOffsets[b];
422             int srcLineOffset = srcBandOffsets[b];
423 
424             // Cache the references to avoid extra indexing.
425             float[] x = abscissas[b];
426             float[] gain = slopes[b];
427             float[] bias = intercepts[b];
428             float yL = minOrdinates[b];
429             float yH = maxOrdinates[b];
430 
431             for (int h = 0; h < dstHeight; h++) {
432                 int dstPixelOffset = dstLineOffset;
433                 int srcPixelOffset = srcLineOffset;
434 
435                 dstLineOffset += dstLineStride;
436                 srcLineOffset += srcLineStride;
437 
438                 for (int w = 0; w < dstWidth; w++) {
439                     d[dstPixelOffset] =
440                         ImageUtil.clampRoundInt(binarySearch(x, yL, yH, gain, bias,
441                                                    s[srcPixelOffset]));
442 
443                     dstPixelOffset += dstPixelStride;
444                     srcPixelOffset += srcPixelStride;
445                 }
446             }
447         }
448     }
449 
computeRectFloat(RasterAccessor src, RasterAccessor dst)450     private void computeRectFloat(RasterAccessor src,
451                                   RasterAccessor dst) {
452         int dstWidth = dst.getWidth();
453         int dstHeight = dst.getHeight();
454         int dstBands = dst.getNumBands();
455 
456         int dstLineStride = dst.getScanlineStride();
457         int dstPixelStride = dst.getPixelStride();
458         int[] dstBandOffsets = dst.getBandOffsets();
459         float[][] dstData = dst.getFloatDataArrays();
460 
461         int srcLineStride = src.getScanlineStride();
462         int srcPixelStride = src.getPixelStride();
463         int[] srcBandOffsets = src.getBandOffsets();
464         float[][] srcData = src.getFloatDataArrays();
465 
466         for (int b = 0; b < dstBands; b++) {
467             float[] d = dstData[b];
468             float[] s = srcData[b];
469 
470             int dstLineOffset = dstBandOffsets[b];
471             int srcLineOffset = srcBandOffsets[b];
472 
473             // Cache the references to avoid extra indexing.
474             float[] x = abscissas[b];
475             float[] gain = slopes[b];
476             float[] bias = intercepts[b];
477             float yL = minOrdinates[b];
478             float yH = maxOrdinates[b];
479 
480             for (int h = 0; h < dstHeight; h++) {
481                 int dstPixelOffset = dstLineOffset;
482                 int srcPixelOffset = srcLineOffset;
483 
484                 dstLineOffset += dstLineStride;
485                 srcLineOffset += srcLineStride;
486 
487                 for (int w = 0; w < dstWidth; w++) {
488                     d[dstPixelOffset] =
489                         binarySearch(x, yL, yH, gain, bias,
490                                      s[srcPixelOffset]);
491 
492                     dstPixelOffset += dstPixelStride;
493                     srcPixelOffset += srcPixelStride;
494                 }
495             }
496         }
497     }
498 
computeRectDouble(RasterAccessor src, RasterAccessor dst)499     private void computeRectDouble(RasterAccessor src,
500                                    RasterAccessor dst) {
501         int dstWidth = dst.getWidth();
502         int dstHeight = dst.getHeight();
503         int dstBands = dst.getNumBands();
504 
505         int dstLineStride = dst.getScanlineStride();
506         int dstPixelStride = dst.getPixelStride();
507         int[] dstBandOffsets = dst.getBandOffsets();
508         double[][] dstData = dst.getDoubleDataArrays();
509 
510         int srcLineStride = src.getScanlineStride();
511         int srcPixelStride = src.getPixelStride();
512         int[] srcBandOffsets = src.getBandOffsets();
513         double[][] srcData = src.getDoubleDataArrays();
514 
515         for (int b = 0; b < dstBands; b++) {
516             double[] d = dstData[b];
517             double[] s = srcData[b];
518 
519             int dstLineOffset = dstBandOffsets[b];
520             int srcLineOffset = srcBandOffsets[b];
521 
522             // Cache the references to avoid extra indexing.
523             float[] x = abscissas[b];
524             float[] gain = slopes[b];
525             float[] bias = intercepts[b];
526             float yL = minOrdinates[b];
527             float yH = maxOrdinates[b];
528 
529             for (int h = 0; h < dstHeight; h++) {
530                 int dstPixelOffset = dstLineOffset;
531                 int srcPixelOffset = srcLineOffset;
532 
533                 dstLineOffset += dstLineStride;
534                 srcLineOffset += srcLineStride;
535 
536                 for (int w = 0; w < dstWidth; w++) {
537                     d[dstPixelOffset] =
538                         binarySearch(x, yL, yH, gain, bias,
539                                      (float)s[srcPixelOffset]);
540 
541                     dstPixelOffset += dstPixelStride;
542                     srcPixelOffset += srcPixelStride;
543                 }
544             }
545         }
546     }
547 }
548