1 /*
2  * $RCSfile: AddConstOpImage.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:12 $
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.RasterAccessor;
22 import com.lightcrafts.mediax.jai.RasterFormatTag;
23 import java.util.Map;
24 import com.lightcrafts.media.jai.util.ImageUtil;
25 // import com.lightcrafts.media.jai.test.OpImageTester;
26 
27 /**
28  * An <code>OpImage</code> implementing the "AddConst" operation.
29  *
30  * <p>This <code>OpImage</code> adds a set of constants, one for each
31  * band of the source image, to the pixels of a rendered image.
32  * The destination pixel values are calculated as:
33  * <pre>
34  *     for (int h = 0; h < dstHeight; h++) {
35  *         for (int w = 0; w < dstWidth; w++) {
36  *             for (int b = 0; b < dstNumBands; b++) {
37  *                 if (constants.length < dstNumBands) {
38  *                     dst[h][w][b] = srcs[h][w][b] + constants[0];
39  *                 } else {
40  *                     dst[h][w][b] = srcs[h][w][b] + constants[b];
41  *                 }
42  *             }
43  *         }
44  *     }
45  * </pre>
46  *
47  * @see com.lightcrafts.mediax.jai.operator.AddConstDescriptor
48  * @see AddConstCRIF
49  *
50  */
51 final class AddConstOpImage extends ColormapOpImage {
52 
53     /** The constants to be added, one for each band. */
54     protected double[] constants;
55     private byte[][] byteTable = null;
56 
initByteTable()57     private synchronized void initByteTable() {
58 
59 	if (byteTable != null) {
60 	    return;
61 	}
62 
63         int nbands = constants.length;
64 
65         byteTable = new byte[nbands][256];
66 
67         // Initialize table which implements AddConst and clamp
68         for(int band=0; band<nbands; band++) {
69             int k = ImageUtil.clampRoundInt(constants[band]);
70             byte[] t = byteTable[band];
71             for (int i = 0; i < 256; i++) {
72                 int sum = i + k;
73                 if (sum < 0) {
74                     t[i] = (byte)0;
75                 } else if (sum > 255) {
76                     t[i] = (byte)255;
77                 } else {
78                     t[i] = (byte)sum;
79                 }
80             }
81         }
82     }
83 
84     /**
85      * Constructor.
86      *
87      * @param source     The source image.
88      * @param layout     The destination image layout.
89      * @param constants  The constants to be added, stored as reference.
90      */
AddConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants)91     public AddConstOpImage(RenderedImage source,
92                            Map config,
93                            ImageLayout layout,
94                            double[] constants) {
95         super(source, layout, config, true);
96 
97         int numBands = getSampleModel().getNumBands();
98 
99         if (constants.length < numBands) {
100             this.constants = new double[numBands];
101             for (int i = 0; i < numBands; i++) {
102                 this.constants[i] = constants[0];
103             }
104         } else {
105             this.constants = (double[])constants.clone();
106         }
107 
108         // Set flag to permit in-place operation.
109         permitInPlaceOperation();
110 
111         // Initialize the colormap if necessary.
112         initializeColormapOperation();
113     }
114 
115     /**
116      * Transform the colormap according to the rescaling parameters.
117      */
transformColormap(byte[][] colormap)118     protected void transformColormap(byte[][] colormap) {
119 	initByteTable();
120 
121         for(int b = 0; b < 3; b++) {
122             byte[] map = colormap[b];
123 	    byte[] luTable = byteTable[b >= byteTable.length ? 0 : b];
124             int mapSize = map.length;
125 
126             for(int i = 0; i < mapSize; i++) {
127                 map[i] = luTable[(map[i] & 0xFF)];
128             }
129         }
130     }
131 
132 
133     /**
134      * Adds a constant to the pixel values within a specified rectangle.
135      *
136      * @param sources   Cobbled sources, guaranteed to provide all the
137      *                  source data necessary for computing the rectangle.
138      * @param dest      The tile containing the rectangle to be computed.
139      * @param destRect  The rectangle within the tile to be computed.
140      */
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)141     protected void computeRect(Raster[] sources,
142                                WritableRaster dest,
143                                Rectangle destRect) {
144         // Retrieve format tags.
145         RasterFormatTag[] formatTags = getFormatTags();
146 
147         Rectangle srcRect = mapDestRect(destRect, 0);
148 
149         RasterAccessor dst = new RasterAccessor(dest, destRect,
150                                                 formatTags[1], getColorModel());
151         RasterAccessor src = new RasterAccessor(sources[0], srcRect,
152                                                 formatTags[0],
153                                                 getSourceImage(0).getColorModel());
154         switch (dst.getDataType()) {
155         case DataBuffer.TYPE_BYTE:
156             computeRectByte(src, dst);
157             break;
158         case DataBuffer.TYPE_USHORT:
159             computeRectUShort(src, dst);
160             break;
161         case DataBuffer.TYPE_SHORT:
162             computeRectShort(src, dst);
163             break;
164         case DataBuffer.TYPE_INT:
165             computeRectInt(src, dst);
166             break;
167         case DataBuffer.TYPE_FLOAT:
168             computeRectFloat(src, dst);
169             break;
170         case DataBuffer.TYPE_DOUBLE:
171             computeRectDouble(src, dst);
172             break;
173         }
174 
175         if (dst.needsClamping()) {
176             /* Further clamp down to underlying raster data type. */
177             dst.clampDataArrays();
178         }
179         dst.copyDataToRaster();
180     }
181 
computeRectByte(RasterAccessor src, RasterAccessor dst)182     private void computeRectByte(RasterAccessor src,
183                                  RasterAccessor dst) {
184 	initByteTable();
185 
186         int dstWidth = dst.getWidth();
187         int dstHeight = dst.getHeight();
188         int dstBands = dst.getNumBands();
189 
190         int dstLineStride = dst.getScanlineStride();
191         int dstPixelStride = dst.getPixelStride();
192         int[] dstBandOffsets = dst.getBandOffsets();
193         byte[][] dstData = dst.getByteDataArrays();
194 
195         int srcLineStride = src.getScanlineStride();
196         int srcPixelStride = src.getPixelStride();
197         int[] srcBandOffsets = src.getBandOffsets();
198         byte[][] srcData = src.getByteDataArrays();
199 
200         for (int b = 0; b < dstBands; b++) {
201             byte[] d = dstData[b];
202             byte[] s = srcData[b];
203             byte[] clamp = byteTable[b];
204 
205             int dstLineOffset = dstBandOffsets[b];
206             int srcLineOffset = srcBandOffsets[b];
207 
208             for (int h = 0; h < dstHeight; h++) {
209                 int dstPixelOffset = dstLineOffset;
210                 int srcPixelOffset = srcLineOffset;
211 
212                 dstLineOffset += dstLineStride;
213                 srcLineOffset += srcLineStride;
214 
215                 int dstEnd = dstPixelOffset + dstWidth*dstPixelStride;
216                 while(dstPixelOffset < dstEnd) {
217                     d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF];
218 
219                     dstPixelOffset += dstPixelStride;
220                     srcPixelOffset += srcPixelStride;
221                 }
222             }
223         }
224     }
225 
computeRectUShort(RasterAccessor src, RasterAccessor dst)226     private void computeRectUShort(RasterAccessor src,
227                                    RasterAccessor dst) {
228         int dstWidth = dst.getWidth();
229         int dstHeight = dst.getHeight();
230         int dstBands = dst.getNumBands();
231 
232         int dstLineStride = dst.getScanlineStride();
233         int dstPixelStride = dst.getPixelStride();
234         int[] dstBandOffsets = dst.getBandOffsets();
235         short[][] dstData = dst.getShortDataArrays();
236 
237         int srcLineStride = src.getScanlineStride();
238         int srcPixelStride = src.getPixelStride();
239         int[] srcBandOffsets = src.getBandOffsets();
240         short[][] srcData = src.getShortDataArrays();
241 
242         for (int b = 0; b < dstBands; b++) {
243             int c = ImageUtil.clampRoundInt(constants[b]);
244             short[] d = dstData[b];
245             short[] s = srcData[b];
246 
247             int dstLineOffset = dstBandOffsets[b];
248             int srcLineOffset = srcBandOffsets[b];
249 
250             for (int h = 0; h < dstHeight; h++) {
251                 int dstPixelOffset = dstLineOffset;
252                 int srcPixelOffset = srcLineOffset;
253 
254                 dstLineOffset += dstLineStride;
255                 srcLineOffset += srcLineStride;
256 
257                 for (int w = 0; w < dstWidth; w++) {
258                     d[dstPixelOffset] = ImageUtil.clampUShort(
259                                         (s[srcPixelOffset] & 0xFFFF) + c);
260 
261                     dstPixelOffset += dstPixelStride;
262                     srcPixelOffset += srcPixelStride;
263                 }
264             }
265         }
266     }
267 
computeRectShort(RasterAccessor src, RasterAccessor dst)268     private void computeRectShort(RasterAccessor src,
269                                   RasterAccessor dst) {
270         int dstWidth = dst.getWidth();
271         int dstHeight = dst.getHeight();
272         int dstBands = dst.getNumBands();
273 
274         int dstLineStride = dst.getScanlineStride();
275         int dstPixelStride = dst.getPixelStride();
276         int[] dstBandOffsets = dst.getBandOffsets();
277         short[][] dstData = dst.getShortDataArrays();
278 
279         int srcLineStride = src.getScanlineStride();
280         int srcPixelStride = src.getPixelStride();
281         int[] srcBandOffsets = src.getBandOffsets();
282         short[][] srcData = src.getShortDataArrays();
283 
284         for (int b = 0; b < dstBands; b++) {
285             int c = ImageUtil.clampRoundInt(constants[b]);
286             short[] d = dstData[b];
287             short[] s = srcData[b];
288 
289             int dstLineOffset = dstBandOffsets[b];
290             int srcLineOffset = srcBandOffsets[b];
291 
292             for (int h = 0; h < dstHeight; h++) {
293                 int dstPixelOffset = dstLineOffset;
294                 int srcPixelOffset = srcLineOffset;
295 
296                 dstLineOffset += dstLineStride;
297                 srcLineOffset += srcLineStride;
298 
299                 for (int w = 0; w < dstWidth; w++) {
300                     d[dstPixelOffset] = ImageUtil.clampShort(s[srcPixelOffset] + c);
301 
302                     dstPixelOffset += dstPixelStride;
303                     srcPixelOffset += srcPixelStride;
304                 }
305             }
306         }
307     }
308 
computeRectInt(RasterAccessor src, RasterAccessor dst)309     private void computeRectInt(RasterAccessor src,
310                                 RasterAccessor dst) {
311         int dstWidth = dst.getWidth();
312         int dstHeight = dst.getHeight();
313         int dstBands = dst.getNumBands();
314 
315         int dstLineStride = dst.getScanlineStride();
316         int dstPixelStride = dst.getPixelStride();
317         int[] dstBandOffsets = dst.getBandOffsets();
318         int[][] dstData = dst.getIntDataArrays();
319 
320         int srcLineStride = src.getScanlineStride();
321         int srcPixelStride = src.getPixelStride();
322         int[] srcBandOffsets = src.getBandOffsets();
323         int[][] srcData = src.getIntDataArrays();
324 
325         for (int b = 0; b < dstBands; b++) {
326             long c = ImageUtil.clampRoundInt(constants[b]);
327             int[] d = dstData[b];
328             int[] s = srcData[b];
329 
330             int dstLineOffset = dstBandOffsets[b];
331             int srcLineOffset = srcBandOffsets[b];
332 
333             for (int h = 0; h < dstHeight; h++) {
334                 int dstPixelOffset = dstLineOffset;
335                 int srcPixelOffset = srcLineOffset;
336 
337                 dstLineOffset += dstLineStride;
338                 srcLineOffset += srcLineStride;
339 
340                 for (int w = 0; w < dstWidth; w++) {
341                     d[dstPixelOffset] = ImageUtil.clampInt(s[srcPixelOffset] + c);
342 
343                     dstPixelOffset += dstPixelStride;
344                     srcPixelOffset += srcPixelStride;
345                 }
346             }
347         }
348     }
349 
computeRectFloat(RasterAccessor src, RasterAccessor dst)350     private void computeRectFloat(RasterAccessor src,
351                                   RasterAccessor dst) {
352         int dstWidth = dst.getWidth();
353         int dstHeight = dst.getHeight();
354         int dstBands = dst.getNumBands();
355 
356         int dstLineStride = dst.getScanlineStride();
357         int dstPixelStride = dst.getPixelStride();
358         int[] dstBandOffsets = dst.getBandOffsets();
359         float[][] dstData = dst.getFloatDataArrays();
360 
361         int srcLineStride = src.getScanlineStride();
362         int srcPixelStride = src.getPixelStride();
363         int[] srcBandOffsets = src.getBandOffsets();
364         float[][] srcData = src.getFloatDataArrays();
365 
366         for (int b = 0; b < dstBands; b++) {
367             double c = constants[b];
368             float[] d = dstData[b];
369             float[] s = srcData[b];
370 
371             int dstLineOffset = dstBandOffsets[b];
372             int srcLineOffset = srcBandOffsets[b];
373 
374             for (int h = 0; h < dstHeight; h++) {
375                 int dstPixelOffset = dstLineOffset;
376                 int srcPixelOffset = srcLineOffset;
377 
378                 dstLineOffset += dstLineStride;
379                 srcLineOffset += srcLineStride;
380 
381                 for (int w = 0; w < dstWidth; w++) {
382                     d[dstPixelOffset] = ImageUtil.clampFloat(s[srcPixelOffset] + c);
383 
384                     dstPixelOffset += dstPixelStride;
385                     srcPixelOffset += srcPixelStride;
386                 }
387             }
388         }
389     }
390 
computeRectDouble(RasterAccessor src, RasterAccessor dst)391     private void computeRectDouble(RasterAccessor src,
392                                    RasterAccessor dst) {
393         int dstWidth = dst.getWidth();
394         int dstHeight = dst.getHeight();
395         int dstBands = dst.getNumBands();
396 
397         int dstLineStride = dst.getScanlineStride();
398         int dstPixelStride = dst.getPixelStride();
399         int[] dstBandOffsets = dst.getBandOffsets();
400         double[][] dstData = dst.getDoubleDataArrays();
401 
402         int srcLineStride = src.getScanlineStride();
403         int srcPixelStride = src.getPixelStride();
404         int[] srcBandOffsets = src.getBandOffsets();
405         double[][] srcData = src.getDoubleDataArrays();
406 
407         for (int b = 0; b < dstBands; b++) {
408             double c = constants[b];
409             double[] d = dstData[b];
410             double[] s = srcData[b];
411 
412             int dstLineOffset = dstBandOffsets[b];
413             int srcLineOffset = srcBandOffsets[b];
414 
415             for (int h = 0; h < dstHeight; h++) {
416                 int dstPixelOffset = dstLineOffset;
417                 int srcPixelOffset = srcLineOffset;
418 
419                 dstLineOffset += dstLineStride;
420                 srcLineOffset += srcLineStride;
421 
422                 for (int w = 0; w < dstWidth; w++) {
423                     d[dstPixelOffset] = s[srcPixelOffset] + c;
424 
425                     dstPixelOffset += dstPixelStride;
426                     srcPixelOffset += srcPixelStride;
427                 }
428             }
429         }
430     }
431 
432 //     public static void main(String args[]) {
433 //         System.out.println("AddConstOpImage Test");
434 
435 //         ImageLayout layout;
436 //         OpImage src, dst;
437 //         Rectangle rect = new Rectangle(0, 0, 5, 5);
438 
439 //         double[] constants = new double[3];
440 //         constants[0] = 10.0;
441 //         constants[1] = 20.0;
442 //         constants[2] = 30.0;
443 
444 //         System.out.println("1. PixelInterleaved byte 3-band");
445 //         layout = OpImageTester.createImageLayout(
446 //             0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false);
447 //         src = OpImageTester.createRandomOpImage(layout);
448 //         dst = new AddConstOpImage(src, null, null, constants);
449 //         OpImageTester.testOpImage(dst, rect);
450 //         OpImageTester.timeOpImage(dst, 10);
451 
452 // 	System.out.println("3. PixelInterleaved int 3-band");
453 //         layout = OpImageTester.createImageLayout(
454 //             0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false);
455 //         src = OpImageTester.createRandomOpImage(layout);
456 //         dst = new AddConstOpImage(src, null, null, constants);
457 //         OpImageTester.testOpImage(dst, rect);
458 //         OpImageTester.timeOpImage(dst, 10);
459 //     }
460 }
461