1 /* Copyright (C) 2005-2011 Fabio Riccardi */
2 
3 /*
4  * $RCSfile: AddOpImage.java,v $
5  *
6  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
7  *
8  * Use is subject to license terms.
9  *
10  * $Revision: 1.1 $
11  * $Date: 2005/02/11 04:56:12 $
12  * $State: Exp $
13  */
14 package com.lightcrafts.jai.opimage;
15 
16 import java.awt.Rectangle;
17 import java.awt.image.DataBuffer;
18 import java.awt.image.Raster;
19 import java.awt.image.RenderedImage;
20 import java.awt.image.SampleModel;
21 import java.awt.image.WritableRaster;
22 import com.lightcrafts.mediax.jai.ImageLayout;
23 import com.lightcrafts.mediax.jai.PointOpImage;
24 import com.lightcrafts.mediax.jai.RasterAccessor;
25 import com.lightcrafts.mediax.jai.RasterFormatTag;
26 import com.lightcrafts.mediax.jai.RasterFactory;
27 import java.util.Map;
28 
29 import com.lightcrafts.media.jai.util.ImageUtil;
30 import com.lightcrafts.media.jai.util.JDKWorkarounds;
31 
32 final class UnSharpMaskOpImage extends PointOpImage {
33 
34     /* Source 1 band increment */
35     private int s1bd = 1;
36 
37     /* Source 2 band increment */
38     private int s2bd = 1;
39 
40     protected double gain;
41     protected int threshold;
42 
43     /**
44      * Constructs an <code>AddOpImage</code>.
45      *
46      * <p>The <code>layout</code> parameter may optionally contains the
47      * tile grid layout, sample model, and/or color model. The image
48      * dimension is determined by the intersection of the bounding boxes
49      * of the two source images.
50      *
51      * <p>The image layout of the first source image, <code>source1</code>,
52      * is used as the fall-back for the image layout of the destination
53      * image. Any layout parameters not specified in the <code>layout</code>
54      * argument are set to the same value as that of <code>source1</code>.
55      *
56      * @param source1  The first source image.
57      * @param source2  The second source image.
58      * @param layout   The destination image layout.
59      */
UnSharpMaskOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout, double gain, int threshold)60     public UnSharpMaskOpImage(RenderedImage source1,
61                               RenderedImage source2,
62                               Map config,
63                               ImageLayout layout,
64                               double gain, int threshold) {
65         super(source1, source2, layout, config, true);
66 
67         this.gain = gain;
68         this.threshold = threshold;
69 
70         // Get the source band counts.
71         int numBands1 = source1.getSampleModel().getNumBands();
72         int numBands2 = source2.getSampleModel().getNumBands();
73 
74         // Handle the special case of adding a single band image to
75         // each band of a multi-band image.
76         int numBandsDst;
77         if (layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) {
78             SampleModel sm = layout.getSampleModel(null);
79             numBandsDst = sm.getNumBands();
80 
81             // One of the sources must be single-banded and the other must
82             // have at most the number of bands in the SampleModel hint.
83             if (numBandsDst > 1 &&
84                 ((numBands1 == 1 && numBands2 > 1) ||
85                  (numBands2 == 1 && numBands1 > 1))) {
86                 // Clamp the destination band count to the number of
87                 // bands in the multi-band source.
88                 numBandsDst = Math.min(Math.max(numBands1, numBands2),
89                                        numBandsDst);
90 
91                 // Create a new SampleModel if necessary.
92                 if (numBandsDst != sampleModel.getNumBands()) {
93                     sampleModel =
94                             RasterFactory.createComponentSampleModel(sm,
95                                                                      sampleModel.getTransferType(),
96                                                                      sampleModel.getWidth(),
97                                                                      sampleModel.getHeight(),
98                                                                      numBandsDst);
99 
100                     if (colorModel != null &&
101                         !JDKWorkarounds.areCompatibleDataModels(sampleModel,
102                                                                 colorModel)) {
103                         colorModel =
104                                 ImageUtil.getCompatibleColorModel(sampleModel,
105                                                                   config);
106                     }
107                 }
108 
109                 // Set the source band increments.
110                 s1bd = numBands1 == 1 ? 0 : 1;
111                 s2bd = numBands2 == 1 ? 0 : 1;
112             }
113         }
114 
115         // Set flag to permit in-place operation.
116         permitInPlaceOperation();
117     }
118 
119     /**
120      * Adds the pixel values of two source images within a specified
121      * rectangle.
122      *
123      * @param sources   Cobbled sources, guaranteed to provide all the
124      *                  source data necessary for computing the rectangle.
125      * @param dest      The tile containing the rectangle to be computed.
126      * @param destRect  The rectangle within the tile to be computed.
127      */
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)128     protected void computeRect(Raster[] sources,
129                                WritableRaster dest,
130                                Rectangle destRect) {
131         // Retrieve format tags.
132         RasterFormatTag[] formatTags = getFormatTags();
133 
134         RasterAccessor s1 = new RasterAccessor(sources[0], destRect,
135                                                formatTags[0],
136                                                getSourceImage(0).getColorModel());
137         RasterAccessor s2 = new RasterAccessor(sources[1], destRect,
138                                                formatTags[1],
139                                                getSourceImage(1).getColorModel());
140         RasterAccessor d = new RasterAccessor(dest, destRect,
141                                               formatTags[2], getColorModel());
142 
143         switch (d.getDataType()) {
144         case DataBuffer.TYPE_BYTE:
145             computeRectByte(s1, s2, d);
146             break;
147         case DataBuffer.TYPE_USHORT:
148             computeRectUShort(s1, s2, d);
149             break;
150         case DataBuffer.TYPE_SHORT:
151             computeRectShort(s1, s2, d);
152             break;
153         case DataBuffer.TYPE_INT:
154             computeRectInt(s1, s2, d);
155             break;
156         case DataBuffer.TYPE_FLOAT:
157             computeRectFloat(s1, s2, d);
158             break;
159         case DataBuffer.TYPE_DOUBLE:
160             computeRectDouble(s1, s2, d);
161             break;
162         }
163 
164         if (d.needsClamping()) {
165             d.clampDataArrays();
166         }
167         d.copyDataToRaster();
168     }
169 
computeRectByte(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst)170     private void computeRectByte(RasterAccessor src1,
171                                  RasterAccessor src2,
172                                  RasterAccessor dst) {
173         int s1LineStride = src1.getScanlineStride();
174         int s1PixelStride = src1.getPixelStride();
175         int[] s1BandOffsets = src1.getBandOffsets();
176         byte[][] s1Data = src1.getByteDataArrays();
177 
178         int s2LineStride = src2.getScanlineStride();
179         int s2PixelStride = src2.getPixelStride();
180         int[] s2BandOffsets = src2.getBandOffsets();
181         byte[][] s2Data = src2.getByteDataArrays();
182 
183         int dwidth = dst.getWidth();
184         int dheight = dst.getHeight();
185         int bands = dst.getNumBands();
186         int dLineStride = dst.getScanlineStride();
187         int dPixelStride = dst.getPixelStride();
188         int[] dBandOffsets = dst.getBandOffsets();
189         byte[][] dData = dst.getByteDataArrays();
190 
191         int c = (int) (gain * 0x100);
192 
193         for (int b = 0, s1b = 0, s2b = 0; b < bands;
194              b++, s1b += s1bd, s2b += s2bd) {
195             byte[] s1 = s1Data[s1b];
196             byte[] s2 = s2Data[s2b];
197             byte[] d = dData[b];
198 
199             int s1LineOffset = s1BandOffsets[s1b];
200             int s2LineOffset = s2BandOffsets[s2b];
201             int dLineOffset = dBandOffsets[b];
202 
203             for (int h = 0; h < dheight; h++) {
204                 int s1PixelOffset = s1LineOffset;
205                 int s2PixelOffset = s2LineOffset;
206                 int dPixelOffset = dLineOffset;
207 
208                 s1LineOffset += s1LineStride;
209                 s2LineOffset += s2LineStride;
210                 dLineOffset += dLineStride;
211 
212                 for (int w = 0; w < dwidth; w++) {
213                     int src = s1[s1PixelOffset] & 0xFF;
214                     d[dPixelOffset] = ImageUtil.clampByte(src + c * (src - (s2[s2PixelOffset] & 0xFF)) / 0x100);
215 
216                     s1PixelOffset += s1PixelStride;
217                     s2PixelOffset += s2PixelStride;
218                     dPixelOffset += dPixelStride;
219                 }
220             }
221         }
222     }
223 
sigmoid(double x)224     private static double sigmoid(double x) {
225         final double s = 0.05;
226         return 1 / (1 + Math.exp(- s * (x + 60)));
227     }
228 
229     static final int sigmoidTableLenght = 16 * 1024;
230     static final float sigmoidTable[] = new float[sigmoidTableLenght];
231 
232     static {
233         for (int i = 0; i < sigmoidTableLenght; i++)
234             sigmoidTable[i] = (float) sigmoid(0.02 * (i - sigmoidTableLenght / 2));
235 
236         /* try {
237             FileOutputStream tableDump = new FileOutputStream("/Stuff/tableDump");
238             for (float entry : sigmoidTable)
239                 tableDump.write((Float.toString(entry) + '\n').getBytes());
240             tableDump.close();
241         } catch (Exception e) {
242             e.printStackTrace();
243         } */
244     }
245 
sigmoidT(double x)246     private static double sigmoidT(double x) {
247         int idx = (int) (50 * x + 0.5) + sigmoidTableLenght / 2;
248         if (idx < 0)
249             return 0;
250         else if (idx >= sigmoidTableLenght)
251             return 1;
252         else
253             return sigmoidTable[idx];
254     }
255 
main( String[] args )256     public static void main( String[] args ) {
257         System.out.println("Here: ");
258     }
259 
computeRectUShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst)260     private void computeRectUShort(RasterAccessor src1,
261                                    RasterAccessor src2,
262                                    RasterAccessor dst) {
263         int s1LineStride = src1.getScanlineStride();
264         int s1PixelStride = src1.getPixelStride();
265         int[] s1BandOffsets = src1.getBandOffsets();
266         short[][] s1Data = src1.getShortDataArrays();
267 
268         int s2LineStride = src2.getScanlineStride();
269         int s2PixelStride = src2.getPixelStride();
270         int[] s2BandOffsets = src2.getBandOffsets();
271         short[][] s2Data = src2.getShortDataArrays();
272 
273         int dwidth = dst.getWidth();
274         int dheight = dst.getHeight();
275         int bands = dst.getNumBands();
276         int dLineStride = dst.getScanlineStride();
277         int dPixelStride = dst.getPixelStride();
278         int[] dBandOffsets = dst.getBandOffsets();
279         short[][] dData = dst.getShortDataArrays();
280 
281         int c = (int) (gain * 256);
282         int t = 256 * threshold;
283 
284         short[] s1 = s1Data[0];
285         short[] s2 = s2Data[0];
286         short[] d = dData[0];
287 
288         int s1LineOffset = s1BandOffsets[0];
289         int s2LineOffset = s2BandOffsets[0];
290         int dLineOffset = dBandOffsets[0];
291 
292         for (int h = 0; h < dheight; h++) {
293             int s1PixelOffset = s1LineOffset;
294             int s2PixelOffset = s2LineOffset;
295             int dPixelOffset = dLineOffset;
296 
297             s1LineOffset += s1LineStride;
298             s2LineOffset += s2LineStride;
299             dLineOffset += dLineStride;
300 
301             for (int w = 0; w < dwidth; w++) {
302                 if (bands == 3) {
303                     int s10 = s1[s1PixelOffset+0] & 0xFFFF;
304                     int s20 = s2[s2PixelOffset+0] & 0xFFFF;
305                     int d0 = s10 - s20;
306 
307                     int s11 = s1[s1PixelOffset+1] & 0xFFFF;
308                     int s21 = s2[s1PixelOffset+1] & 0xFFFF;
309                     int d1 = s11 - s21;
310 
311                     int s12 = s1[s1PixelOffset+2] & 0xFFFF;
312                     int s22 = s2[s1PixelOffset+2] & 0xFFFF;
313                     int d2 = s12 - s22;
314 
315                     double diff = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
316                     double s = sigmoidT(20 * diff - t);
317 
318                     d[dPixelOffset+0] = ImageUtil.clampUShort(s10 + (int) (c * d0 * s / 256.));
319                     d[dPixelOffset+1] = ImageUtil.clampUShort(s11 + (int) (c * d1 * s / 256.));
320                     d[dPixelOffset+2] = ImageUtil.clampUShort(s12 + (int) (c * d2 * s / 256.));
321                 } else {
322                     int ss1 = s1[s1PixelOffset] & 0xFFFF;
323                     int ss2 = s2[s1PixelOffset] & 0xFFFF;
324                     int dd = ss1 - ss2;
325 
326                     double s = sigmoidT(20 * Math.abs(ss1 - ss2) - t);
327 
328                     d[dPixelOffset] = ImageUtil.clampUShort(ss1 + (int) (c * dd * s / 256.));
329                 }
330                 s1PixelOffset += s1PixelStride;
331                 s2PixelOffset += s2PixelStride;
332                 dPixelOffset += dPixelStride;
333             }
334         }
335     }
336 
computeRectShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst)337     private void computeRectShort(RasterAccessor src1,
338                                   RasterAccessor src2,
339                                   RasterAccessor dst) {
340         int s1LineStride = src1.getScanlineStride();
341         int s1PixelStride = src1.getPixelStride();
342         int[] s1BandOffsets = src1.getBandOffsets();
343         short[][] s1Data = src1.getShortDataArrays();
344 
345         int s2LineStride = src2.getScanlineStride();
346         int s2PixelStride = src2.getPixelStride();
347         int[] s2BandOffsets = src2.getBandOffsets();
348         short[][] s2Data = src2.getShortDataArrays();
349 
350         int dwidth = dst.getWidth();
351         int dheight = dst.getHeight();
352         int bands = dst.getNumBands();
353         int dLineStride = dst.getScanlineStride();
354         int dPixelStride = dst.getPixelStride();
355         int[] dBandOffsets = dst.getBandOffsets();
356         short[][] dData = dst.getShortDataArrays();
357 
358         for (int b = 0, s1b = 0, s2b = 0; b < bands;
359              b++, s1b += s1bd, s2b += s2bd) {
360             short[] s1 = s1Data[s1b];
361             short[] s2 = s2Data[s2b];
362             short[] d = dData[b];
363             int c = (int) (gain * (Short.MAX_VALUE + 1) + 0.5);
364 
365             int s1LineOffset = s1BandOffsets[s1b];
366             int s2LineOffset = s2BandOffsets[s2b];
367             int dLineOffset = dBandOffsets[b];
368 
369             for (int h = 0; h < dheight; h++) {
370                 int s1PixelOffset = s1LineOffset;
371                 int s2PixelOffset = s2LineOffset;
372                 int dPixelOffset = dLineOffset;
373 
374                 s1LineOffset += s1LineStride;
375                 s2LineOffset += s2LineStride;
376                 dLineOffset += dLineStride;
377 
378                 for (int w = 0; w < dwidth; w++) {
379                     int src = s1[s1PixelOffset];
380                     d[dPixelOffset] = ImageUtil.clampRoundShort(src + c * (src - s2[s2PixelOffset]) / (Short.MAX_VALUE + 1));
381 
382                     s1PixelOffset += s1PixelStride;
383                     s2PixelOffset += s2PixelStride;
384                     dPixelOffset += dPixelStride;
385                 }
386             }
387         }
388     }
389 
computeRectInt(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst)390     private void computeRectInt(RasterAccessor src1,
391                                 RasterAccessor src2,
392                                 RasterAccessor dst) {
393         int s1LineStride = src1.getScanlineStride();
394         int s1PixelStride = src1.getPixelStride();
395         int[] s1BandOffsets = src1.getBandOffsets();
396         int[][] s1Data = src1.getIntDataArrays();
397 
398         int s2LineStride = src2.getScanlineStride();
399         int s2PixelStride = src2.getPixelStride();
400         int[] s2BandOffsets = src2.getBandOffsets();
401         int[][] s2Data = src2.getIntDataArrays();
402 
403         int dwidth = dst.getWidth();
404         int dheight = dst.getHeight();
405         int bands = dst.getNumBands();
406         int dLineStride = dst.getScanlineStride();
407         int dPixelStride = dst.getPixelStride();
408         int[] dBandOffsets = dst.getBandOffsets();
409         int[][] dData = dst.getIntDataArrays();
410 
411         /*
412          * The destination data type may be any of the integral data types.
413          * The "clamp" function must clamp to the appropriate range for
414          * that data type.
415          */
416         switch (sampleModel.getTransferType()) {
417         case DataBuffer.TYPE_BYTE:
418             for (int b = 0, s1b = 0, s2b = 0; b < bands;
419                  b++, s1b += s1bd, s2b += s2bd) {
420                 int[] s1 = s1Data[s1b];
421                 int[] s2 = s2Data[s2b];
422                 int[] d = dData[b];
423                 int c = (int) (gain * 0x100 + 0.5);
424 
425                 int s1LineOffset = s1BandOffsets[s1b];
426                 int s2LineOffset = s2BandOffsets[s2b];
427                 int dLineOffset = dBandOffsets[b];
428 
429                 for (int h = 0; h < dheight; h++) {
430                     int s1PixelOffset = s1LineOffset;
431                     int s2PixelOffset = s2LineOffset;
432                     int dPixelOffset = dLineOffset;
433 
434                     s1LineOffset += s1LineStride;
435                     s2LineOffset += s2LineStride;
436                     dLineOffset += dLineStride;
437 
438                     for (int w = 0; w < dwidth; w++) {
439                         int src = s1[s1PixelOffset] & 0xFF;
440                         d[dPixelOffset] = ImageUtil.clampRoundByte(src + c * (src - (s2[s2PixelOffset] & 0xFF)) / 0x100);
441 
442                         s1PixelOffset += s1PixelStride;
443                         s2PixelOffset += s2PixelStride;
444                         dPixelOffset += dPixelStride;
445                     }
446                 }
447             }
448             break;
449 
450         case DataBuffer.TYPE_USHORT:
451             for (int b = 0, s1b = 0, s2b = 0; b < bands;
452                  b++, s1b += s1bd, s2b += s2bd) {
453                 int[] s1 = s1Data[s1b];
454                 int[] s2 = s2Data[s2b];
455                 int[] d = dData[b];
456                 long c = (long) (gain * 0x10000 + 0.5);
457 
458                 int s1LineOffset = s1BandOffsets[s1b];
459                 int s2LineOffset = s2BandOffsets[s2b];
460                 int dLineOffset = dBandOffsets[b];
461 
462                 for (int h = 0; h < dheight; h++) {
463                     int s1PixelOffset = s1LineOffset;
464                     int s2PixelOffset = s2LineOffset;
465                     int dPixelOffset = dLineOffset;
466 
467                     s1LineOffset += s1LineStride;
468                     s2LineOffset += s2LineStride;
469                     dLineOffset += dLineStride;
470 
471                     for (int w = 0; w < dwidth; w++) {
472                         int src = s1[s1PixelOffset] & 0xFFFF;
473                         d[dPixelOffset] = ImageUtil.clampRoundUShort(src + c * (src - (s2[s2PixelOffset] & 0xFFFF)) / 0x10000);
474 
475                         s1PixelOffset += s1PixelStride;
476                         s2PixelOffset += s2PixelStride;
477                         dPixelOffset += dPixelStride;
478                     }
479                 }
480             }
481             break;
482 
483         case DataBuffer.TYPE_SHORT:
484             for (int b = 0, s1b = 0, s2b = 0; b < bands;
485                  b++, s1b += s1bd, s2b += s2bd) {
486                 int[] s1 = s1Data[s1b];
487                 int[] s2 = s2Data[s2b];
488                 int[] d = dData[b];
489                 int c = (int) (gain * (Short.MAX_VALUE + 1) + 0.5);
490 
491                 int s1LineOffset = s1BandOffsets[s1b];
492                 int s2LineOffset = s2BandOffsets[s2b];
493                 int dLineOffset = dBandOffsets[b];
494 
495                 for (int h = 0; h < dheight; h++) {
496                     int s1PixelOffset = s1LineOffset;
497                     int s2PixelOffset = s2LineOffset;
498                     int dPixelOffset = dLineOffset;
499 
500                     s1LineOffset += s1LineStride;
501                     s2LineOffset += s2LineStride;
502                     dLineOffset += dLineStride;
503 
504                     for (int w = 0; w < dwidth; w++) {
505                         int src = s1[s1PixelOffset];
506                         d[dPixelOffset] = ImageUtil.clampRoundShort(src + c * (src - s2[s2PixelOffset]) / (Short.MAX_VALUE + 1));
507 
508                         s1PixelOffset += s1PixelStride;
509                         s2PixelOffset += s2PixelStride;
510                         dPixelOffset += dPixelStride;
511                     }
512                 }
513             }
514             break;
515 
516         case DataBuffer.TYPE_INT:
517             for (int b = 0, s1b = 0, s2b = 0; b < bands;
518                  b++, s1b += s1bd, s2b += s2bd) {
519                 int[] s1 = s1Data[s1b];
520                 int[] s2 = s2Data[s2b];
521                 int[] d = dData[b];
522                 long c = (long) (gain * ((long) Integer.MAX_VALUE + 1) + 0.5);
523 
524                 int s1LineOffset = s1BandOffsets[s1b];
525                 int s2LineOffset = s2BandOffsets[s2b];
526                 int dLineOffset = dBandOffsets[b];
527 
528                 for (int h = 0; h < dheight; h++) {
529                     int s1PixelOffset = s1LineOffset;
530                     int s2PixelOffset = s2LineOffset;
531                     int dPixelOffset = dLineOffset;
532 
533                     s1LineOffset += s1LineStride;
534                     s2LineOffset += s2LineStride;
535                     dLineOffset += dLineStride;
536 
537                     for (int w = 0; w < dwidth; w++) {
538                         int src = s1[s1PixelOffset];
539                         d[dPixelOffset] = ImageUtil.clampRoundInt(src + c * (src - s2[s2PixelOffset]) / ((long) Integer.MAX_VALUE + 1));
540 
541                         s1PixelOffset += s1PixelStride;
542                         s2PixelOffset += s2PixelStride;
543                         dPixelOffset += dPixelStride;
544                     }
545                 }
546             }
547             break;
548         }
549     }
550 
computeRectFloat(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst)551     private void computeRectFloat(RasterAccessor src1,
552                                   RasterAccessor src2,
553                                   RasterAccessor dst) {
554         int s1LineStride = src1.getScanlineStride();
555         int s1PixelStride = src1.getPixelStride();
556         int[] s1BandOffsets = src1.getBandOffsets();
557         float[][] s1Data = src1.getFloatDataArrays();
558 
559         int s2LineStride = src2.getScanlineStride();
560         int s2PixelStride = src2.getPixelStride();
561         int[] s2BandOffsets = src2.getBandOffsets();
562         float[][] s2Data = src2.getFloatDataArrays();
563 
564         int dwidth = dst.getWidth();
565         int dheight = dst.getHeight();
566         int bands = dst.getNumBands();
567         int dLineStride = dst.getScanlineStride();
568         int dPixelStride = dst.getPixelStride();
569         int[] dBandOffsets = dst.getBandOffsets();
570         float[][] dData = dst.getFloatDataArrays();
571 
572         for (int b = 0, s1b = 0, s2b = 0; b < bands;
573              b++, s1b += s1bd, s2b += s2bd) {
574             float[] s1 = s1Data[s1b];
575             float[] s2 = s2Data[s2b];
576             float[] d = dData[b];
577             float c = (float) gain;
578 
579             int s1LineOffset = s1BandOffsets[s1b];
580             int s2LineOffset = s2BandOffsets[s2b];
581             int dLineOffset = dBandOffsets[b];
582 
583             for (int h = 0; h < dheight; h++) {
584                 int s1PixelOffset = s1LineOffset;
585                 int s2PixelOffset = s2LineOffset;
586                 int dPixelOffset = dLineOffset;
587 
588                 s1LineOffset += s1LineStride;
589                 s2LineOffset += s2LineStride;
590                 dLineOffset += dLineStride;
591 
592                 for (int w = 0; w < dwidth; w++) {
593                     float src = s1[s1PixelOffset];
594                     d[dPixelOffset] = src + c * (src - s2[s2PixelOffset]);
595 
596                     s1PixelOffset += s1PixelStride;
597                     s2PixelOffset += s2PixelStride;
598                     dPixelOffset += dPixelStride;
599                 }
600             }
601         }
602     }
603 
computeRectDouble(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst)604     private void computeRectDouble(RasterAccessor src1,
605                                    RasterAccessor src2,
606                                    RasterAccessor dst) {
607         int s1LineStride = src1.getScanlineStride();
608         int s1PixelStride = src1.getPixelStride();
609         int[] s1BandOffsets = src1.getBandOffsets();
610         double[][] s1Data = src1.getDoubleDataArrays();
611 
612         int s2LineStride = src2.getScanlineStride();
613         int s2PixelStride = src2.getPixelStride();
614         int[] s2BandOffsets = src2.getBandOffsets();
615         double[][] s2Data = src2.getDoubleDataArrays();
616 
617         int dwidth = dst.getWidth();
618         int dheight = dst.getHeight();
619         int bands = dst.getNumBands();
620         int dLineStride = dst.getScanlineStride();
621         int dPixelStride = dst.getPixelStride();
622         int[] dBandOffsets = dst.getBandOffsets();
623         double[][] dData = dst.getDoubleDataArrays();
624 
625         for (int b = 0, s1b = 0, s2b = 0; b < bands;
626              b++, s1b += s1bd, s2b += s2bd) {
627             double[] s1 = s1Data[s1b];
628             double[] s2 = s2Data[s2b];
629             double[] d = dData[b];
630             double c = gain;
631 
632             int s1LineOffset = s1BandOffsets[s1b];
633             int s2LineOffset = s2BandOffsets[s2b];
634             int dLineOffset = dBandOffsets[b];
635 
636             for (int h = 0; h < dheight; h++) {
637                 int s1PixelOffset = s1LineOffset;
638                 int s2PixelOffset = s2LineOffset;
639                 int dPixelOffset = dLineOffset;
640 
641                 s1LineOffset += s1LineStride;
642                 s2LineOffset += s2LineStride;
643                 dLineOffset += dLineStride;
644 
645                 for (int w = 0; w < dwidth; w++) {
646                     double src = s1[s1PixelOffset];
647                     d[dPixelOffset] = src + c * (src - s2[s2PixelOffset]);
648 
649                     s1PixelOffset += s1PixelStride;
650                     s2PixelOffset += s2PixelStride;
651                     dPixelOffset += dPixelStride;
652                 }
653             }
654         }
655     }
656 }
657