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