1 /* 2 * $RCSfile: RescaleOpImage.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:41 $ 10 * $State: Exp $ 11 */ 12 package com.lightcrafts.media.jai.opimage; 13 import java.awt.Rectangle; 14 import java.awt.image.DataBuffer; 15 import java.awt.image.Raster; 16 import java.awt.image.RenderedImage; 17 import java.awt.image.WritableRaster; 18 19 import com.lightcrafts.mediax.jai.ColormapOpImage; 20 import com.lightcrafts.mediax.jai.ImageLayout; 21 import com.lightcrafts.mediax.jai.RasterAccessor; 22 import com.lightcrafts.mediax.jai.RasterFormatTag; 23 24 import java.util.Collection; 25 import java.util.LinkedList; 26 import java.util.Map; 27 import java.util.concurrent.Callable; 28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.Executors; 30 31 import com.lightcrafts.media.jai.util.ImageUtil; 32 33 /** 34 * An <code>OpImage</code> implementing the "Rescale" operation. 35 * 36 * <p> The "Rescale" operation maps the pixel values of an image from 37 * one range to another range by multiplying each pixel value by one 38 * of a set of constants and then adding another constant to the 39 * result of the multiplication. The pixel values of the destination 40 * image are defined by the pseudocode: 41 * 42 * <pre> 43 * for (int h = 0; h < dstHeight; h++) { 44 * for (int w = 0; w < dstWidth; w++) { 45 * for (int b = 0; b < dstNumBands; b++) { 46 * scale = (scales.length < dstNumBands)? 47 * scales[0]:scales[b]; 48 * offset = (offsets.length < dstNumBands)? 49 * offsets[0]:offsets[b]; 50 * dst[h][w][b] = srcs[h][w][b] * scale + offset; 51 * } 52 * } 53 * } 54 * </pre> 55 * 56 * @see com.lightcrafts.mediax.jai.operator.RescaleDescriptor 57 * @see RescaleCRIF 58 * 59 * 60 * @since EA3 61 */ 62 final class RescaleOpImage extends ColormapOpImage { 63 64 /** The constants to be multiplied, one for each band. */ 65 protected double[] constants; 66 protected double[] offsets; 67 68 private byte[][] byteTable = null; 69 70 static final int numProc = Runtime.getRuntime().availableProcessors(); 71 initByteTable()72 private synchronized void initByteTable() { 73 74 if (byteTable != null) { 75 return; 76 } 77 78 int nbands = constants.length; 79 80 byteTable = new byte[nbands][256]; 81 82 // Initialize table which implements Rescale and clamp 83 for(int band=0; band<nbands; band++) { 84 byte[] t = byteTable[band]; 85 double c = constants[band]; 86 double o = offsets[band]; 87 for (int i = 0; i < 256; i++) { 88 t[i] = ImageUtil.clampRoundByte(i * c + o); 89 } 90 } 91 } 92 93 /** 94 * Constructor. 95 * 96 * @param source The source image. 97 * @param config Configurable attributes of the image including 98 * configuration variables indexed by 99 * <code>RenderingHints.Key</code>s and image properties indexed 100 * by <code>String</code>s or <code>CaselessStringKey</code>s. 101 * This is simply forwarded to the superclass constructor. 102 * @param layout The destination image layout. 103 * @param constants The constants to be multiplied, stored as reference. 104 * @param offsets The offsets to be added, stored as reference. 105 */ RescaleOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants, double[] offsets)106 public RescaleOpImage(RenderedImage source, 107 Map config, 108 ImageLayout layout, 109 double[] constants, 110 double[] offsets) { 111 super(source, layout, config, true); 112 113 int numBands = getSampleModel().getNumBands(); 114 115 if (constants.length < numBands) { 116 this.constants = new double[numBands]; 117 for (int i = 0; i < numBands; i++) { 118 this.constants[i] = constants[0]; 119 } 120 } else { 121 this.constants = constants; 122 } 123 124 if (offsets.length < numBands) { 125 this.offsets = new double[numBands]; 126 for (int i = 0; i < numBands; i++) { 127 this.offsets[i] = offsets[0]; 128 } 129 } else { 130 this.offsets = offsets; 131 } 132 133 // Set flag to permit in-place operation. 134 permitInPlaceOperation(); 135 136 // Initialize the colormap if necessary. 137 initializeColormapOperation(); 138 } 139 140 /** 141 * Transform the colormap according to the rescaling parameters. 142 */ transformColormap(byte[][] colormap)143 protected void transformColormap(byte[][] colormap) { 144 for (int b = 0; b < 3; b++) { 145 byte[] map = colormap[b]; 146 int mapSize = map.length; 147 148 float c = (float)(b < constants.length ? 149 constants[b] : constants[0]); 150 float o = (float)(b < constants.length ? 151 offsets[b] : offsets[0]); 152 153 for (int i = 0; i < mapSize; i++) { 154 map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) * c + o); 155 } 156 } 157 } 158 159 /** 160 * Rescales to the pixel values within a specified rectangle. 161 * 162 * @param sources Cobbled sources, guaranteed to provide all the 163 * source data necessary for computing the rectangle. 164 * @param dest The tile containing the rectangle to be computed. 165 * @param destRect The rectangle within the tile to be computed. 166 */ computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)167 protected void computeRect(Raster[] sources, 168 WritableRaster dest, 169 Rectangle destRect) { 170 // Retrieve format tags. 171 RasterFormatTag[] formatTags = getFormatTags(); 172 173 Rectangle srcRect = mapDestRect(destRect, 0); 174 175 RasterAccessor dst = new RasterAccessor(dest, destRect, 176 formatTags[1], getColorModel()); 177 RasterAccessor src = new RasterAccessor(sources[0], srcRect, 178 formatTags[0], 179 getSourceImage(0).getColorModel()); 180 181 switch (dst.getDataType()) { 182 case DataBuffer.TYPE_BYTE: 183 computeRectByte(src, dst); 184 break; 185 case DataBuffer.TYPE_USHORT: 186 computeRectUShort(src, dst); 187 break; 188 case DataBuffer.TYPE_SHORT: 189 computeRectShort(src, dst); 190 break; 191 case DataBuffer.TYPE_INT: 192 computeRectInt(src, dst); 193 break; 194 case DataBuffer.TYPE_FLOAT: 195 computeRectFloat(src, dst); 196 break; 197 case DataBuffer.TYPE_DOUBLE: 198 computeRectDouble(src, dst); 199 break; 200 } 201 202 if (dst.needsClamping()) { 203 /* Further clamp down to underlying raster data type. */ 204 dst.clampDataArrays(); 205 } 206 dst.copyDataToRaster(); 207 } 208 computeRectByte(RasterAccessor src, RasterAccessor dst)209 private void computeRectByte(RasterAccessor src, 210 RasterAccessor dst) { 211 final int dstWidth = dst.getWidth(); 212 final int dstHeight = dst.getHeight(); 213 final int dstBands = dst.getNumBands(); 214 215 final int dstLineStride = dst.getScanlineStride(); 216 final int dstPixelStride = dst.getPixelStride(); 217 final int[] dstBandOffsets = dst.getBandOffsets(); 218 final byte[][] dstData = dst.getByteDataArrays(); 219 220 final int srcLineStride = src.getScanlineStride(); 221 final int srcPixelStride = src.getPixelStride(); 222 final int[] srcBandOffsets = src.getBandOffsets(); 223 final byte[][] srcData = src.getByteDataArrays(); 224 225 initByteTable(); 226 227 ExecutorService threadPool = Executors.newFixedThreadPool(numProc); 228 Collection<Callable<Void>> processes = new LinkedList<Callable<Void>>(); 229 for (int band = 0; band < dstBands; band++) { 230 final byte[] s = srcData[band]; 231 final byte[] d = dstData[band]; 232 233 final int dstLineOffset = dstBandOffsets[band]; 234 final int srcLineOffset = srcBandOffsets[band]; 235 236 final byte[] clamp = byteTable[band]; 237 238 for (int h = 0; h < dstHeight; h++) { 239 final int hh = h; 240 processes.add(new Callable<Void>() { 241 @Override 242 public Void call() { 243 int dstPixelOffset = dstLineOffset + hh * dstLineStride; 244 int srcPixelOffset = srcLineOffset + hh * srcLineStride; 245 246 for (int w = 0; w < dstWidth; w++) { 247 d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF]; 248 249 dstPixelOffset += dstPixelStride; 250 srcPixelOffset += srcPixelStride; 251 } 252 return null; 253 } 254 }); 255 } 256 } 257 try { 258 threadPool.invokeAll(processes); 259 } catch (InterruptedException e) { 260 throw new RuntimeException(e); 261 } finally { 262 threadPool.shutdown(); 263 } 264 } 265 computeRectUShort(RasterAccessor src, RasterAccessor dst)266 private void computeRectUShort(RasterAccessor src, 267 RasterAccessor dst) { 268 final int dstWidth = dst.getWidth(); 269 final int dstHeight = dst.getHeight(); 270 final int dstBands = dst.getNumBands(); 271 272 final int dstLineStride = dst.getScanlineStride(); 273 final int dstPixelStride = dst.getPixelStride(); 274 final int[] dstBandOffsets = dst.getBandOffsets(); 275 final short[][] dstData = dst.getShortDataArrays(); 276 277 final int srcLineStride = src.getScanlineStride(); 278 final int srcPixelStride = src.getPixelStride(); 279 final int[] srcBandOffsets = src.getBandOffsets(); 280 final short[][] srcData = src.getShortDataArrays(); 281 282 ExecutorService threadPool = Executors.newFixedThreadPool(numProc); 283 Collection<Callable<Void>> processes = new LinkedList<Callable<Void>>(); 284 for (int band = 0; band < dstBands; band++) { 285 final float c = (float)constants[band]; 286 final float o = (float)offsets[band]; 287 final short[] s = srcData[band]; 288 final short[] d = dstData[band]; 289 290 final int dstLineOffset = dstBandOffsets[band]; 291 final int srcLineOffset = srcBandOffsets[band]; 292 293 for (int h = 0; h < dstHeight; h++) { 294 final int hh = h; 295 processes.add(new Callable<Void>() { 296 @Override 297 public Void call() { 298 int dstPixelOffset = dstLineOffset + hh * dstLineStride; 299 int srcPixelOffset = srcLineOffset + hh * srcLineStride; 300 301 for (int w = 0; w < dstWidth; w++) { 302 d[dstPixelOffset] = ImageUtil.clampRoundUShort( 303 (s[srcPixelOffset] & 0xFFFF) * c + o); 304 305 dstPixelOffset += dstPixelStride; 306 srcPixelOffset += srcPixelStride; 307 } 308 return null; 309 } 310 }); 311 } 312 } 313 try { 314 threadPool.invokeAll(processes); 315 } catch (InterruptedException e) { 316 throw new RuntimeException(e); 317 } finally { 318 threadPool.shutdown(); 319 } 320 } 321 computeRectShort(RasterAccessor src, RasterAccessor dst)322 private void computeRectShort(RasterAccessor src, 323 RasterAccessor dst) { 324 final int dstWidth = dst.getWidth(); 325 final int dstHeight = dst.getHeight(); 326 final int dstBands = dst.getNumBands(); 327 328 final int dstLineStride = dst.getScanlineStride(); 329 final int dstPixelStride = dst.getPixelStride(); 330 final int[] dstBandOffsets = dst.getBandOffsets(); 331 final short[][] dstData = dst.getShortDataArrays(); 332 333 final int srcLineStride = src.getScanlineStride(); 334 final int srcPixelStride = src.getPixelStride(); 335 final int[] srcBandOffsets = src.getBandOffsets(); 336 final short[][] srcData = src.getShortDataArrays(); 337 338 ExecutorService threadPool = Executors.newFixedThreadPool(numProc); 339 Collection<Callable<Void>> processes = new LinkedList<Callable<Void>>(); 340 for (int band = 0; band < dstBands; band++) { 341 final float c = (float)constants[band]; 342 final float o = (float)offsets[band]; 343 final short[] s = srcData[band]; 344 final short[] d = dstData[band]; 345 346 final int dstLineOffset = dstBandOffsets[band]; 347 final int srcLineOffset = srcBandOffsets[band]; 348 349 for (int h = 0; h < dstHeight; h++) { 350 final int hh = h; 351 processes.add(new Callable<Void>() { 352 @Override 353 public Void call() { 354 int dstPixelOffset = dstLineOffset + hh * dstLineStride; 355 int srcPixelOffset = srcLineOffset + hh * srcLineStride; 356 357 for (int w = 0; w < dstWidth; w++) { 358 d[dstPixelOffset] = ImageUtil.clampRoundShort(s[srcPixelOffset] * c + o); 359 360 dstPixelOffset += dstPixelStride; 361 srcPixelOffset += srcPixelStride; 362 } 363 return null; 364 } 365 }); 366 } 367 } 368 try { 369 threadPool.invokeAll(processes); 370 } catch (InterruptedException e) { 371 throw new RuntimeException(e); 372 } finally { 373 threadPool.shutdown(); 374 } 375 } 376 computeRectInt(RasterAccessor src, RasterAccessor dst)377 private void computeRectInt(RasterAccessor src, 378 RasterAccessor dst) { 379 final int dstWidth = dst.getWidth(); 380 final int dstHeight = dst.getHeight(); 381 final int dstBands = dst.getNumBands(); 382 383 final int dstLineStride = dst.getScanlineStride(); 384 final int dstPixelStride = dst.getPixelStride(); 385 final int[] dstBandOffsets = dst.getBandOffsets(); 386 final int[][] dstData = dst.getIntDataArrays(); 387 388 final int srcLineStride = src.getScanlineStride(); 389 final int srcPixelStride = src.getPixelStride(); 390 final int[] srcBandOffsets = src.getBandOffsets(); 391 final int[][] srcData = src.getIntDataArrays(); 392 393 ExecutorService threadPool = Executors.newFixedThreadPool(numProc); 394 Collection<Callable<Void>> processes = new LinkedList<Callable<Void>>(); 395 for (int b = 0; b < dstBands; b++) { 396 final double c = constants[b]; 397 final double o = offsets[b]; 398 final int[] s = srcData[b]; 399 final int[] d = dstData[b]; 400 401 final int dstLineOffset = dstBandOffsets[b]; 402 final int srcLineOffset = srcBandOffsets[b]; 403 404 for (int h = 0; h < dstHeight; h++) { 405 final int hh = h; 406 processes.add(new Callable<Void>() { 407 @Override 408 public Void call() { 409 int dstPixelOffset = dstLineOffset + hh * dstLineStride; 410 int srcPixelOffset = srcLineOffset + hh * srcLineStride; 411 412 for (int w = 0; w < dstWidth; w++) { 413 d[dstPixelOffset] = ImageUtil.clampRoundInt(s[srcPixelOffset] * c + o); 414 415 dstPixelOffset += dstPixelStride; 416 srcPixelOffset += srcPixelStride; 417 } 418 return null; 419 } 420 }); 421 } 422 } 423 try { 424 threadPool.invokeAll(processes); 425 } catch (InterruptedException e) { 426 throw new RuntimeException(e); 427 } finally { 428 threadPool.shutdown(); 429 } 430 } 431 computeRectFloat(RasterAccessor src, RasterAccessor dst)432 private void computeRectFloat(RasterAccessor src, 433 RasterAccessor dst) { 434 final int dstWidth = dst.getWidth(); 435 final int dstHeight = dst.getHeight(); 436 final int dstBands = dst.getNumBands(); 437 438 final int dstLineStride = dst.getScanlineStride(); 439 final int dstPixelStride = dst.getPixelStride(); 440 final int[] dstBandOffsets = dst.getBandOffsets(); 441 final float[][] dstData = dst.getFloatDataArrays(); 442 443 final int srcLineStride = src.getScanlineStride(); 444 final int srcPixelStride = src.getPixelStride(); 445 final int[] srcBandOffsets = src.getBandOffsets(); 446 final float[][] srcData = src.getFloatDataArrays(); 447 448 ExecutorService threadPool = Executors.newFixedThreadPool(numProc); 449 Collection<Callable<Void>> processes = new LinkedList<Callable<Void>>(); 450 for (int band = 0; band < dstBands; band++) { 451 final double c = constants[band]; 452 final double o = offsets[band]; 453 final float[] s = srcData[band]; 454 final float[] d = dstData[band]; 455 456 final int dstLineOffset = dstBandOffsets[band]; 457 final int srcLineOffset = srcBandOffsets[band]; 458 459 for (int h = 0; h < dstHeight; h++) { 460 final int hh = h; 461 processes.add(new Callable<Void>() { 462 @Override 463 public Void call() { 464 int dstPixelOffset = dstLineOffset + hh * dstLineStride; 465 int srcPixelOffset = srcLineOffset + hh * srcLineStride; 466 467 for (int w = 0; w < dstWidth; w++) { 468 d[dstPixelOffset] = ImageUtil.clampFloat(s[srcPixelOffset] * c + o); 469 470 dstPixelOffset += dstPixelStride; 471 srcPixelOffset += srcPixelStride; 472 } 473 return null; 474 } 475 }); 476 } 477 } 478 try { 479 threadPool.invokeAll(processes); 480 } catch (InterruptedException e) { 481 throw new RuntimeException(e); 482 } finally { 483 threadPool.shutdown(); 484 } 485 } 486 computeRectDouble(RasterAccessor src, RasterAccessor dst)487 private void computeRectDouble(RasterAccessor src, 488 RasterAccessor dst) { 489 final int dstWidth = dst.getWidth(); 490 final int dstHeight = dst.getHeight(); 491 final int dstBands = dst.getNumBands(); 492 493 final int dstLineStride = dst.getScanlineStride(); 494 final int dstPixelStride = dst.getPixelStride(); 495 final int[] dstBandOffsets = dst.getBandOffsets(); 496 final double[][] dstData = dst.getDoubleDataArrays(); 497 498 final int srcLineStride = src.getScanlineStride(); 499 final int srcPixelStride = src.getPixelStride(); 500 final int[] srcBandOffsets = src.getBandOffsets(); 501 final double[][] srcData = src.getDoubleDataArrays(); 502 503 ExecutorService threadPool = Executors.newFixedThreadPool(numProc); 504 Collection<Callable<Void>> processes = new LinkedList<Callable<Void>>(); 505 for (int band = 0; band < dstBands; band++) { 506 final double c = constants[band]; 507 final double o = offsets[band]; 508 final double[] s = srcData[band]; 509 final double[] d = dstData[band]; 510 511 final int dstLineOffset = dstBandOffsets[band]; 512 final int srcLineOffset = srcBandOffsets[band]; 513 514 for (int h = 0; h < dstHeight; h++) { 515 final int hh = h; 516 processes.add(new Callable<Void>() { 517 @Override 518 public Void call() { 519 int dstPixelOffset = dstLineOffset + hh * dstLineStride; 520 int srcPixelOffset = srcLineOffset + hh * srcLineStride; 521 522 for (int w = 0; w < dstWidth; w++) { 523 d[dstPixelOffset] = s[srcPixelOffset] * c + o; 524 525 dstPixelOffset += dstPixelStride; 526 srcPixelOffset += srcPixelStride; 527 } 528 return null; 529 } 530 }); 531 } 532 } 533 try { 534 threadPool.invokeAll(processes); 535 } catch (InterruptedException e) { 536 throw new RuntimeException(e); 537 } finally { 538 threadPool.shutdown(); 539 } 540 } 541 } 542