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