1 /* 2 * $RCSfile: ScaleNearestOpImage.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:43 $ 10 * $State: Exp $ 11 */ 12 package com.lightcrafts.media.jai.opimage; 13 import java.awt.Rectangle; 14 import java.awt.image.ColorModel; 15 import java.awt.image.DataBuffer; 16 import java.awt.image.IndexColorModel; 17 import java.awt.image.Raster; 18 import java.awt.image.RenderedImage; 19 import java.awt.image.WritableRaster; 20 import com.lightcrafts.mediax.jai.Interpolation; 21 import com.lightcrafts.mediax.jai.ImageLayout; 22 import com.lightcrafts.mediax.jai.RasterAccessor; 23 import com.lightcrafts.mediax.jai.RasterFormatTag; 24 import com.lightcrafts.mediax.jai.ScaleOpImage; 25 import java.util.Map; 26 import com.lightcrafts.mediax.jai.BorderExtender; 27 import com.lightcrafts.media.jai.util.Rational; 28 // import com.lightcrafts.media.jai.test.OpImageTester; 29 30 /** 31 * An OpImage subclass that performs nearest-neighbor scaling. 32 * 33 */ 34 final class ScaleNearestOpImage extends ScaleOpImage { 35 36 long invScaleXInt, invScaleXFrac; 37 long invScaleYInt, invScaleYFrac; 38 39 /** 40 * Constructs a ScaleNearestOpImage from a RenderedImage source, 41 * 42 * @param source a RenderedImage. 43 * @param layout an ImageLayout optionally containing the tile grid layout, 44 * SampleModel, and ColorModel, or null. 45 * @param xScale scale factor along x axis. 46 * @param yScale scale factor along y axis. 47 * @param xTrans translation factor along x axis. 48 * @param yTrans translation factor along y axis. 49 * @param interp an Interpolation object to use for resampling. 50 */ ScaleNearestOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp)51 public ScaleNearestOpImage(RenderedImage source, 52 BorderExtender extender, 53 Map config, 54 ImageLayout layout, 55 float xScale, 56 float yScale, 57 float xTrans, 58 float yTrans, 59 Interpolation interp) { 60 super(source, 61 layout, 62 config, 63 true, 64 extender, 65 interp, 66 xScale, 67 yScale, 68 xTrans, 69 yTrans); 70 71 // If the source has an IndexColorModel, override the default setting 72 // in OpImage. The dest shall have exactly the same SampleModel and 73 // ColorModel as the source. 74 // Note, in this case, the source should have an integral data type. 75 ColorModel srcColorModel = source.getColorModel(); 76 if (srcColorModel instanceof IndexColorModel) { 77 sampleModel = source.getSampleModel().createCompatibleSampleModel( 78 tileWidth, tileHeight); 79 colorModel = srcColorModel; 80 } 81 82 if (invScaleXRational.num > invScaleXRational.denom) { 83 invScaleXInt = invScaleXRational.num / invScaleXRational.denom; 84 invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; 85 } else { 86 invScaleXInt = 0; 87 invScaleXFrac = invScaleXRational.num; 88 } 89 90 if (invScaleYRational.num > invScaleYRational.denom) { 91 invScaleYInt = invScaleYRational.num / invScaleYRational.denom; 92 invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; 93 } else { 94 invScaleYInt = 0; 95 invScaleYFrac = invScaleYRational.num; 96 } 97 } 98 99 /** 100 * Performs a scale operation on a specified rectangle. The sources are 101 * cobbled. 102 * 103 * @param sources an array of source Rasters, guaranteed to provide all 104 * necessary source data for computing the output. 105 * @param dest a WritableRaster containing the area to be computed. 106 * @param destRect the rectangle within dest to be processed. 107 */ computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect)108 protected void computeRect(Raster [] sources, 109 WritableRaster dest, 110 Rectangle destRect) { 111 // Retrieve format tags. 112 RasterFormatTag[] formatTags = getFormatTags(); 113 114 Raster source = sources[0]; 115 116 // Get the source rectangle 117 Rectangle srcRect = source.getBounds(); 118 119 int srcRectX = srcRect.x; 120 int srcRectY = srcRect.y; 121 122 RasterAccessor srcAccessor = 123 new RasterAccessor(source, srcRect, formatTags[0], 124 getSource(0).getColorModel()); 125 126 RasterAccessor dstAccessor = 127 new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); 128 129 int srcScanlineStride = srcAccessor.getScanlineStride(); 130 int srcPixelStride = srcAccessor.getPixelStride(); 131 132 // Destination rectangle dimensions. 133 int dx = destRect.x; 134 int dy = destRect.y; 135 int dwidth = destRect.width; 136 int dheight = destRect.height; 137 138 // Precalculate the x positions and store them in an array. 139 int[] xvalues = new int[dwidth]; 140 141 long sxNum = dx, sxDenom = 1; 142 143 // Subtract the X translation factor sx -= transX 144 sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; 145 sxDenom *= transXRationalDenom; 146 147 // Add 0.5 148 sxNum = 2 * sxNum + sxDenom; 149 sxDenom *= 2; 150 151 // Multply by invScaleX 152 sxNum *= invScaleXRationalNum; 153 sxDenom *= invScaleXRationalDenom; 154 155 // Separate the x source coordinate into integer and fractional part 156 // int part is floor(sx), frac part is sx - floor(sx) 157 int srcXInt = Rational.floor(sxNum , sxDenom); 158 long srcXFrac = sxNum % sxDenom; 159 if (srcXInt < 0) { 160 srcXFrac = sxDenom + srcXFrac; 161 } 162 163 // Normalize - Get a common denominator for the fracs of 164 // src and invScaleX 165 long commonXDenom = sxDenom * invScaleXRationalDenom; 166 srcXFrac *= invScaleXRationalDenom; 167 long newInvScaleXFrac = invScaleXFrac * sxDenom; 168 169 for (int i = 0; i < dwidth; i++) { 170 171 // Calculate the position 172 xvalues[i] = (srcXInt - srcRectX) * srcPixelStride; 173 174 // Move onto the next source pixel. 175 176 // Add the integral part of invScaleX to the integral part 177 // of srcX 178 srcXInt += invScaleXInt; 179 180 // Add the fractional part of invScaleX to the fractional part 181 // of srcX 182 srcXFrac += newInvScaleXFrac; 183 184 // If the fractional part is now greater than equal to the 185 // denominator, divide so as to reduce the numerator to be less 186 // than the denominator and add the overflow to the integral part. 187 if (srcXFrac >= commonXDenom) { 188 srcXInt += 1; 189 srcXFrac -= commonXDenom; 190 } 191 } 192 193 // Precalculate the y positions and store them in an array. 194 int[] yvalues = new int[dheight]; 195 196 long syNum = dy, syDenom = 1; 197 198 // Subtract the X translation factor sy -= transY 199 syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; 200 syDenom *= transYRationalDenom; 201 202 // Add 0.5 203 syNum = 2 * syNum + syDenom; 204 syDenom *= 2; 205 206 // Multply by invScaleX 207 syNum *= invScaleYRationalNum; 208 syDenom *= invScaleYRationalDenom; 209 210 // Separate the x source coordinate into integer and fractional part 211 int srcYInt = Rational.floor(syNum , syDenom); 212 long srcYFrac = syNum % syDenom; 213 if (srcYInt < 0) { 214 srcYFrac = syDenom + srcYFrac; 215 } 216 217 // Normalize - Get a common denominator for the fracs of 218 // src and invScaleY 219 long commonYDenom = syDenom * invScaleYRationalDenom; 220 srcYFrac *= invScaleYRationalDenom; 221 long newInvScaleYFrac = invScaleYFrac * syDenom; 222 223 for (int i = 0; i < dheight; i++) { 224 225 // Calculate the position 226 yvalues[i] = (srcYInt - srcRectY) * srcScanlineStride; 227 228 // Move onto the next source pixel. 229 230 // Add the integral part of invScaleY to the integral part 231 // of srcY 232 srcYInt += invScaleYInt; 233 234 // Add the fractional part of invScaleY to the fractional part 235 // of srcY 236 srcYFrac += newInvScaleYFrac; 237 238 // If the fractional part is now greater than equal to the 239 // denominator, divide so as to reduce the numerator to be less 240 // than the denominator and add the overflow to the integral part. 241 if (srcYFrac >= commonYDenom) { 242 srcYInt += 1; 243 srcYFrac -= commonYDenom; 244 } 245 } 246 247 switch (dstAccessor.getDataType()) { 248 249 case DataBuffer.TYPE_BYTE: 250 byteLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); 251 break; 252 253 case DataBuffer.TYPE_SHORT: 254 case DataBuffer.TYPE_USHORT: 255 shortLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); 256 break; 257 258 case DataBuffer.TYPE_INT: 259 intLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); 260 break; 261 262 case DataBuffer.TYPE_FLOAT: 263 floatLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); 264 break; 265 266 case DataBuffer.TYPE_DOUBLE: 267 doubleLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); 268 break; 269 270 default: 271 throw new 272 RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); 273 } 274 275 // If the RasterAccessor object set up a temporary buffer for the 276 // op to write to, tell the RasterAccessor to write that data 277 // to the raster no that we're done with it. 278 if (dstAccessor.isDataCopy()) { 279 dstAccessor.clampDataArrays(); 280 dstAccessor.copyDataToRaster(); 281 } 282 } 283 byteLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[])284 private void byteLoop(RasterAccessor src, Rectangle dstRect, 285 RasterAccessor dst, int xvalues[], int yvalues[]) { 286 287 int dwidth = dstRect.width; 288 int dheight = dstRect.height; 289 290 // Get destination related variables. 291 byte dstDataArrays[][] = dst.getByteDataArrays(); 292 int dstBandOffsets[] = dst.getBandOffsets(); 293 int dstPixelStride = dst.getPixelStride(); 294 int dstScanlineStride = dst.getScanlineStride(); 295 int dnumBands = dst.getNumBands(); 296 297 // Get source related variables. 298 int bandOffsets[] = src.getBandOffsets(); 299 byte srcDataArrays[][] = src.getByteDataArrays(); 300 301 int dstPixelOffset; 302 int dstOffset = 0; 303 int posy, posx, pos; 304 305 int dstScanlineOffset; 306 // For each band 307 for (int k = 0; k < dnumBands; k++) { 308 byte dstData[] = dstDataArrays[k]; 309 byte srcData[] = srcDataArrays[k]; 310 int bandOffset = bandOffsets[k]; 311 dstScanlineOffset = dstBandOffsets[k]; 312 for (int j = 0; j < dheight; j++) { 313 dstPixelOffset = dstScanlineOffset; 314 posy = yvalues[j] + bandOffset; 315 for (int i = 0; i < dwidth; i++) { 316 posx = xvalues[i]; 317 pos = posx + posy; 318 dstData[dstPixelOffset] = srcData[pos]; 319 dstPixelOffset += dstPixelStride; 320 } 321 dstScanlineOffset += dstScanlineStride; 322 } 323 } 324 } 325 shortLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[])326 private void shortLoop(RasterAccessor src, Rectangle dstRect, 327 RasterAccessor dst, int xvalues[], int yvalues[]) { 328 329 int dwidth = dstRect.width; 330 int dheight = dstRect.height; 331 332 // Get destination related variables. 333 short dstDataArrays[][] = dst.getShortDataArrays(); 334 int dstBandOffsets[] = dst.getBandOffsets(); 335 int dstPixelStride = dst.getPixelStride(); 336 int dstScanlineStride = dst.getScanlineStride(); 337 int dnumBands = dst.getNumBands(); 338 339 // Get source related variables. 340 int bandOffsets[] = src.getBandOffsets(); 341 short srcDataArrays[][] = src.getShortDataArrays(); 342 343 int dstPixelOffset; 344 int dstOffset = 0; 345 int posy, posx, pos; 346 347 int dstScanlineOffset; 348 // For each band 349 for (int k = 0; k < dnumBands; k++) { 350 short dstData[] = dstDataArrays[k]; 351 short srcData[] = srcDataArrays[k]; 352 int bandOffset = bandOffsets[k]; 353 dstScanlineOffset = dstBandOffsets[k]; 354 for (int j = 0; j < dheight; j++) { 355 dstPixelOffset = dstScanlineOffset; 356 posy = yvalues[j] + bandOffset; 357 for (int i = 0; i < dwidth; i++) { 358 posx = xvalues[i]; 359 pos = posx + posy; 360 dstData[dstPixelOffset] = srcData[pos]; 361 dstPixelOffset += dstPixelStride; 362 } 363 dstScanlineOffset += dstScanlineStride; 364 } 365 } 366 } 367 368 // identical to byteLoops, except datatypes have changed. clumsy, 369 // but there's no other way in Java intLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[])370 private void intLoop(RasterAccessor src, Rectangle dstRect, 371 RasterAccessor dst, int xvalues[], int yvalues[]) { 372 373 int dwidth = dstRect.width; 374 int dheight = dstRect.height; 375 376 int dnumBands = dst.getNumBands(); 377 int dstDataArrays[][] = dst.getIntDataArrays(); 378 int dstBandOffsets[] = dst.getBandOffsets(); 379 int dstPixelStride = dst.getPixelStride(); 380 int dstScanlineStride = dst.getScanlineStride(); 381 382 int bandOffsets[] = src.getBandOffsets(); 383 int srcDataArrays[][] = src.getIntDataArrays(); 384 385 int dstPixelOffset; 386 int dstOffset = 0; 387 int posy, posx, pos; 388 389 int dstScanlineOffset; 390 // For each band 391 for (int k = 0; k < dnumBands; k++) { 392 int dstData[] = dstDataArrays[k]; 393 int srcData[] = srcDataArrays[k]; 394 int bandOffset = bandOffsets[k]; 395 dstScanlineOffset = dstBandOffsets[k]; 396 for (int j = 0; j < dheight; j++) { 397 dstPixelOffset = dstScanlineOffset; 398 posy = yvalues[j] + bandOffset; 399 for (int i = 0; i < dwidth; i++) { 400 posx = xvalues[i]; 401 pos = posx + posy; 402 dstData[dstPixelOffset] = srcData[pos]; 403 dstPixelOffset += dstPixelStride; 404 } 405 dstScanlineOffset += dstScanlineStride; 406 } 407 } 408 } 409 410 // identical to byteLoop, except datatypes have changed. clumsy, 411 // but there's no other way in Java floatLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[])412 private void floatLoop(RasterAccessor src, Rectangle dstRect, 413 RasterAccessor dst, int xvalues[], int yvalues[]) { 414 415 int dwidth = dstRect.width; 416 int dheight = dstRect.height; 417 418 int dnumBands = dst.getNumBands(); 419 float dstDataArrays[][] = dst.getFloatDataArrays(); 420 int dstBandOffsets[] = dst.getBandOffsets(); 421 int dstPixelStride = dst.getPixelStride(); 422 int dstScanlineStride = dst.getScanlineStride(); 423 424 float srcDataArrays[][] = src.getFloatDataArrays(); 425 int bandOffsets[] = src.getBandOffsets(); 426 427 int dstPixelOffset; 428 int dstOffset = 0; 429 int posy, posx, pos; 430 431 int dstScanlineOffset; 432 // For each band 433 for (int k = 0; k < dnumBands; k++) { 434 float dstData[] = dstDataArrays[k]; 435 float srcData[] = srcDataArrays[k]; 436 int bandOffset = bandOffsets[k]; 437 dstScanlineOffset = dstBandOffsets[k]; 438 for (int j = 0; j < dheight; j++) { 439 dstPixelOffset = dstScanlineOffset; 440 posy = yvalues[j] + bandOffset; 441 for (int i = 0; i < dwidth; i++) { 442 posx = xvalues[i]; 443 pos = posx + posy; 444 dstData[dstPixelOffset] = srcData[pos]; 445 dstPixelOffset += dstPixelStride; 446 } 447 dstScanlineOffset += dstScanlineStride; 448 } 449 } 450 } 451 452 // identical to byteLoop, except datatypes have changed. clumsy, 453 // but there's no other way in Java doubleLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[])454 private void doubleLoop(RasterAccessor src, Rectangle dstRect, 455 RasterAccessor dst, int xvalues[], int yvalues[]) { 456 457 int dwidth = dstRect.width; 458 int dheight = dstRect.height; 459 460 int dnumBands = dst.getNumBands(); 461 double dstDataArrays[][] = dst.getDoubleDataArrays(); 462 int dstBandOffsets[] = dst.getBandOffsets(); 463 int dstPixelStride = dst.getPixelStride(); 464 int dstScanlineStride = dst.getScanlineStride(); 465 466 int bandOffsets[] = src.getBandOffsets(); 467 double srcDataArrays[][] = src.getDoubleDataArrays(); 468 469 int dstPixelOffset; 470 int dstOffset = 0; 471 int posy, posx, pos; 472 473 int dstScanlineOffset; 474 // For each band 475 for (int k = 0; k < dnumBands; k++) { 476 double dstData[] = dstDataArrays[k]; 477 double srcData[] = srcDataArrays[k]; 478 int bandOffset = bandOffsets[k]; 479 dstScanlineOffset = dstBandOffsets[k]; 480 for (int j = 0; j < dheight; j++) { 481 dstPixelOffset = dstScanlineOffset; 482 posy = yvalues[j] + bandOffset; 483 for (int i = 0; i < dwidth; i++) { 484 posx = xvalues[i]; 485 pos = posx + posy; 486 dstData[dstPixelOffset] = srcData[pos]; 487 dstPixelOffset += dstPixelStride; 488 } 489 dstScanlineOffset += dstScanlineStride; 490 } 491 } 492 } 493 494 // public static OpImage createTestImage(OpImageTester oit) { 495 // Interpolation interp = 496 // Interpolation.getInstance(Interpolation.INTERP_NEAREST); 497 // return new ScaleNearestOpImage(oit.getSource(), null, 498 // new ImageLayout(oit.getSource()), 499 // 2.5F, 2.5F, 0.0F, 0.0F, 500 // interp); 501 // } 502 503 // public static void main(String args[]) { 504 505 // String classname = "com.lightcrafts.media.jai.opimage.ScaleNearestOpImage"; 506 // OpImageTester.performDiagnostics(classname, args); 507 // System.exit(1); 508 509 // System.out.println("ScaleOpImage Test"); 510 // ImageLayout layout; 511 // OpImage src, dst; 512 // Rectangle rect = new Rectangle(0, 0, 5, 5); 513 514 // InterpolationNearest interp = new InterpolationNearest(); 515 516 // System.out.println("1. PixelInterleaved short 3-band"); 517 // layout = OpImageTester.createImageLayout( 518 // 0, 0, 200, 200, 0, 0, 64, 64, DataBuffer.TYPE_SHORT, 3, false); 519 // src = OpImageTester.createRandomOpImage(layout); 520 // dst = new ScaleNearestOpImage(src, null, null, 521 // 2.0F, 2.0F, 0.0F, 0.0F, interp); 522 // OpImageTester.testOpImage(dst, rect); 523 // OpImageTester.timeOpImage(dst, 10); 524 525 // System.out.println("2. PixelInterleaved ushort 3-band"); 526 // layout = OpImageTester.createImageLayout( 527 // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_USHORT, 3, false); 528 // src = OpImageTester.createRandomOpImage(layout); 529 // dst = new ScaleNearestOpImage(src, null, null, 530 // 4.0F, 2.0F, 0.0F, 0.0F, interp); 531 // OpImageTester.testOpImage(dst, rect); 532 // OpImageTester.timeOpImage(dst, 10); 533 // } 534 } 535