001: /*
002: * $RCSfile: ScaleNearestOpImage.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:56:43 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.image.ColorModel;
016: import java.awt.image.DataBuffer;
017: import java.awt.image.IndexColorModel;
018: import java.awt.image.Raster;
019: import java.awt.image.RenderedImage;
020: import java.awt.image.WritableRaster;
021: import java.awt.image.renderable.ParameterBlock;
022: import javax.media.jai.Interpolation;
023: import javax.media.jai.InterpolationNearest;
024: import javax.media.jai.ImageLayout;
025: import javax.media.jai.OpImage;
026: import javax.media.jai.PlanarImage;
027: import javax.media.jai.RasterAccessor;
028: import javax.media.jai.RasterFormatTag;
029: import javax.media.jai.ScaleOpImage;
030: import java.util.Map;
031: import javax.media.jai.BorderExtender;
032: import com.sun.media.jai.util.Rational;
033:
034: // import com.sun.media.jai.test.OpImageTester;
035:
036: /**
037: * An OpImage subclass that performs nearest-neighbor scaling.
038: *
039: */
040: final class ScaleNearestOpImage extends ScaleOpImage {
041:
042: long invScaleXInt, invScaleXFrac;
043: long invScaleYInt, invScaleYFrac;
044:
045: /**
046: * Constructs a ScaleNearestOpImage from a RenderedImage source,
047: *
048: * @param source a RenderedImage.
049: * @param layout an ImageLayout optionally containing the tile grid layout,
050: * SampleModel, and ColorModel, or null.
051: * @param xScale scale factor along x axis.
052: * @param yScale scale factor along y axis.
053: * @param xTrans translation factor along x axis.
054: * @param yTrans translation factor along y axis.
055: * @param interp an Interpolation object to use for resampling.
056: */
057: public ScaleNearestOpImage(RenderedImage source,
058: BorderExtender extender, Map config, ImageLayout layout,
059: float xScale, float yScale, float xTrans, float yTrans,
060: Interpolation interp) {
061: super (source, layout, config, true, extender, interp, xScale,
062: yScale, xTrans, yTrans);
063:
064: // If the source has an IndexColorModel, override the default setting
065: // in OpImage. The dest shall have exactly the same SampleModel and
066: // ColorModel as the source.
067: // Note, in this case, the source should have an integral data type.
068: ColorModel srcColorModel = source.getColorModel();
069: if (srcColorModel instanceof IndexColorModel) {
070: sampleModel = source.getSampleModel()
071: .createCompatibleSampleModel(tileWidth, tileHeight);
072: colorModel = srcColorModel;
073: }
074:
075: if (invScaleXRational.num > invScaleXRational.denom) {
076: invScaleXInt = invScaleXRational.num
077: / invScaleXRational.denom;
078: invScaleXFrac = invScaleXRational.num
079: % invScaleXRational.denom;
080: } else {
081: invScaleXInt = 0;
082: invScaleXFrac = invScaleXRational.num;
083: }
084:
085: if (invScaleYRational.num > invScaleYRational.denom) {
086: invScaleYInt = invScaleYRational.num
087: / invScaleYRational.denom;
088: invScaleYFrac = invScaleYRational.num
089: % invScaleYRational.denom;
090: } else {
091: invScaleYInt = 0;
092: invScaleYFrac = invScaleYRational.num;
093: }
094: }
095:
096: /**
097: * Performs a scale operation on a specified rectangle. The sources are
098: * cobbled.
099: *
100: * @param sources an array of source Rasters, guaranteed to provide all
101: * necessary source data for computing the output.
102: * @param dest a WritableRaster containing the area to be computed.
103: * @param destRect the rectangle within dest to be processed.
104: */
105: protected void computeRect(Raster[] sources, WritableRaster dest,
106: Rectangle destRect) {
107: // Retrieve format tags.
108: RasterFormatTag[] formatTags = getFormatTags();
109:
110: Raster source = sources[0];
111:
112: // Get the source rectangle
113: Rectangle srcRect = source.getBounds();
114:
115: int srcRectX = srcRect.x;
116: int srcRectY = srcRect.y;
117:
118: RasterAccessor srcAccessor = new RasterAccessor(source,
119: srcRect, formatTags[0], getSource(0).getColorModel());
120:
121: RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
122: formatTags[1], getColorModel());
123:
124: int srcScanlineStride = srcAccessor.getScanlineStride();
125: int srcPixelStride = srcAccessor.getPixelStride();
126:
127: // Destination rectangle dimensions.
128: int dx = destRect.x;
129: int dy = destRect.y;
130: int dwidth = destRect.width;
131: int dheight = destRect.height;
132:
133: // Precalculate the x positions and store them in an array.
134: int[] xvalues = new int[dwidth];
135:
136: long sxNum = dx, sxDenom = 1;
137:
138: // Subtract the X translation factor sx -= transX
139: sxNum = sxNum * transXRationalDenom - transXRationalNum
140: * sxDenom;
141: sxDenom *= transXRationalDenom;
142:
143: // Add 0.5
144: sxNum = 2 * sxNum + sxDenom;
145: sxDenom *= 2;
146:
147: // Multply by invScaleX
148: sxNum *= invScaleXRationalNum;
149: sxDenom *= invScaleXRationalDenom;
150:
151: // Separate the x source coordinate into integer and fractional part
152: // int part is floor(sx), frac part is sx - floor(sx)
153: int srcXInt = Rational.floor(sxNum, sxDenom);
154: long srcXFrac = sxNum % sxDenom;
155: if (srcXInt < 0) {
156: srcXFrac = sxDenom + srcXFrac;
157: }
158:
159: // Normalize - Get a common denominator for the fracs of
160: // src and invScaleX
161: long commonXDenom = sxDenom * invScaleXRationalDenom;
162: srcXFrac *= invScaleXRationalDenom;
163: long newInvScaleXFrac = invScaleXFrac * sxDenom;
164:
165: for (int i = 0; i < dwidth; i++) {
166:
167: // Calculate the position
168: xvalues[i] = (srcXInt - srcRectX) * srcPixelStride;
169:
170: // Move onto the next source pixel.
171:
172: // Add the integral part of invScaleX to the integral part
173: // of srcX
174: srcXInt += invScaleXInt;
175:
176: // Add the fractional part of invScaleX to the fractional part
177: // of srcX
178: srcXFrac += newInvScaleXFrac;
179:
180: // If the fractional part is now greater than equal to the
181: // denominator, divide so as to reduce the numerator to be less
182: // than the denominator and add the overflow to the integral part.
183: if (srcXFrac >= commonXDenom) {
184: srcXInt += 1;
185: srcXFrac -= commonXDenom;
186: }
187: }
188:
189: // Precalculate the y positions and store them in an array.
190: int[] yvalues = new int[dheight];
191:
192: long syNum = dy, syDenom = 1;
193:
194: // Subtract the X translation factor sy -= transY
195: syNum = syNum * transYRationalDenom - transYRationalNum
196: * syDenom;
197: syDenom *= transYRationalDenom;
198:
199: // Add 0.5
200: syNum = 2 * syNum + syDenom;
201: syDenom *= 2;
202:
203: // Multply by invScaleX
204: syNum *= invScaleYRationalNum;
205: syDenom *= invScaleYRationalDenom;
206:
207: // Separate the x source coordinate into integer and fractional part
208: int srcYInt = Rational.floor(syNum, syDenom);
209: long srcYFrac = syNum % syDenom;
210: if (srcYInt < 0) {
211: srcYFrac = syDenom + srcYFrac;
212: }
213:
214: // Normalize - Get a common denominator for the fracs of
215: // src and invScaleY
216: long commonYDenom = syDenom * invScaleYRationalDenom;
217: srcYFrac *= invScaleYRationalDenom;
218: long newInvScaleYFrac = invScaleYFrac * syDenom;
219:
220: for (int i = 0; i < dheight; i++) {
221:
222: // Calculate the position
223: yvalues[i] = (srcYInt - srcRectY) * srcScanlineStride;
224:
225: // Move onto the next source pixel.
226:
227: // Add the integral part of invScaleY to the integral part
228: // of srcY
229: srcYInt += invScaleYInt;
230:
231: // Add the fractional part of invScaleY to the fractional part
232: // of srcY
233: srcYFrac += newInvScaleYFrac;
234:
235: // If the fractional part is now greater than equal to the
236: // denominator, divide so as to reduce the numerator to be less
237: // than the denominator and add the overflow to the integral part.
238: if (srcYFrac >= commonYDenom) {
239: srcYInt += 1;
240: srcYFrac -= commonYDenom;
241: }
242: }
243:
244: switch (dstAccessor.getDataType()) {
245:
246: case DataBuffer.TYPE_BYTE:
247: byteLoop(srcAccessor, destRect, dstAccessor, xvalues,
248: yvalues);
249: break;
250:
251: case DataBuffer.TYPE_SHORT:
252: case DataBuffer.TYPE_USHORT:
253: shortLoop(srcAccessor, destRect, dstAccessor, xvalues,
254: yvalues);
255: break;
256:
257: case DataBuffer.TYPE_INT:
258: intLoop(srcAccessor, destRect, dstAccessor, xvalues,
259: yvalues);
260: break;
261:
262: case DataBuffer.TYPE_FLOAT:
263: floatLoop(srcAccessor, destRect, dstAccessor, xvalues,
264: yvalues);
265: break;
266:
267: case DataBuffer.TYPE_DOUBLE:
268: doubleLoop(srcAccessor, destRect, dstAccessor, xvalues,
269: yvalues);
270: break;
271:
272: default:
273: throw new RuntimeException(JaiI18N
274: .getString("OrderedDitherOpImage0"));
275: }
276:
277: // If the RasterAccessor object set up a temporary buffer for the
278: // op to write to, tell the RasterAccessor to write that data
279: // to the raster no that we're done with it.
280: if (dstAccessor.isDataCopy()) {
281: dstAccessor.clampDataArrays();
282: dstAccessor.copyDataToRaster();
283: }
284: }
285:
286: private void byteLoop(RasterAccessor src, Rectangle dstRect,
287: RasterAccessor dst, int xvalues[], int yvalues[]) {
288:
289: int dwidth = dstRect.width;
290: int dheight = dstRect.height;
291:
292: // Get destination related variables.
293: byte dstDataArrays[][] = dst.getByteDataArrays();
294: int dstBandOffsets[] = dst.getBandOffsets();
295: int dstPixelStride = dst.getPixelStride();
296: int dstScanlineStride = dst.getScanlineStride();
297: int dnumBands = dst.getNumBands();
298:
299: // Get source related variables.
300: int bandOffsets[] = src.getBandOffsets();
301: byte srcDataArrays[][] = src.getByteDataArrays();
302:
303: int dstPixelOffset;
304: int dstOffset = 0;
305: int posy, posx, pos;
306:
307: int dstScanlineOffset;
308: // For each band
309: for (int k = 0; k < dnumBands; k++) {
310: byte dstData[] = dstDataArrays[k];
311: byte srcData[] = srcDataArrays[k];
312: int bandOffset = bandOffsets[k];
313: dstScanlineOffset = dstBandOffsets[k];
314: for (int j = 0; j < dheight; j++) {
315: dstPixelOffset = dstScanlineOffset;
316: posy = yvalues[j] + bandOffset;
317: for (int i = 0; i < dwidth; i++) {
318: posx = xvalues[i];
319: pos = posx + posy;
320: dstData[dstPixelOffset] = srcData[pos];
321: dstPixelOffset += dstPixelStride;
322: }
323: dstScanlineOffset += dstScanlineStride;
324: }
325: }
326: }
327:
328: private void shortLoop(RasterAccessor src, Rectangle dstRect,
329: RasterAccessor dst, int xvalues[], int yvalues[]) {
330:
331: int dwidth = dstRect.width;
332: int dheight = dstRect.height;
333:
334: // Get destination related variables.
335: short dstDataArrays[][] = dst.getShortDataArrays();
336: int dstBandOffsets[] = dst.getBandOffsets();
337: int dstPixelStride = dst.getPixelStride();
338: int dstScanlineStride = dst.getScanlineStride();
339: int dnumBands = dst.getNumBands();
340:
341: // Get source related variables.
342: int bandOffsets[] = src.getBandOffsets();
343: short srcDataArrays[][] = src.getShortDataArrays();
344:
345: int dstPixelOffset;
346: int dstOffset = 0;
347: int posy, posx, pos;
348:
349: int dstScanlineOffset;
350: // For each band
351: for (int k = 0; k < dnumBands; k++) {
352: short dstData[] = dstDataArrays[k];
353: short srcData[] = srcDataArrays[k];
354: int bandOffset = bandOffsets[k];
355: dstScanlineOffset = dstBandOffsets[k];
356: for (int j = 0; j < dheight; j++) {
357: dstPixelOffset = dstScanlineOffset;
358: posy = yvalues[j] + bandOffset;
359: for (int i = 0; i < dwidth; i++) {
360: posx = xvalues[i];
361: pos = posx + posy;
362: dstData[dstPixelOffset] = srcData[pos];
363: dstPixelOffset += dstPixelStride;
364: }
365: dstScanlineOffset += dstScanlineStride;
366: }
367: }
368: }
369:
370: // identical to byteLoops, except datatypes have changed. clumsy,
371: // but there's no other way in Java
372: private void intLoop(RasterAccessor src, Rectangle dstRect,
373: RasterAccessor dst, int xvalues[], int yvalues[]) {
374:
375: int dwidth = dstRect.width;
376: int dheight = dstRect.height;
377:
378: int dnumBands = dst.getNumBands();
379: int dstDataArrays[][] = dst.getIntDataArrays();
380: int dstBandOffsets[] = dst.getBandOffsets();
381: int dstPixelStride = dst.getPixelStride();
382: int dstScanlineStride = dst.getScanlineStride();
383:
384: int bandOffsets[] = src.getBandOffsets();
385: int srcDataArrays[][] = src.getIntDataArrays();
386:
387: int dstPixelOffset;
388: int dstOffset = 0;
389: int posy, posx, pos;
390:
391: int dstScanlineOffset;
392: // For each band
393: for (int k = 0; k < dnumBands; k++) {
394: int dstData[] = dstDataArrays[k];
395: int srcData[] = srcDataArrays[k];
396: int bandOffset = bandOffsets[k];
397: dstScanlineOffset = dstBandOffsets[k];
398: for (int j = 0; j < dheight; j++) {
399: dstPixelOffset = dstScanlineOffset;
400: posy = yvalues[j] + bandOffset;
401: for (int i = 0; i < dwidth; i++) {
402: posx = xvalues[i];
403: pos = posx + posy;
404: dstData[dstPixelOffset] = srcData[pos];
405: dstPixelOffset += dstPixelStride;
406: }
407: dstScanlineOffset += dstScanlineStride;
408: }
409: }
410: }
411:
412: // identical to byteLoop, except datatypes have changed. clumsy,
413: // but there's no other way in Java
414: private void floatLoop(RasterAccessor src, Rectangle dstRect,
415: RasterAccessor dst, int xvalues[], int yvalues[]) {
416:
417: int dwidth = dstRect.width;
418: int dheight = dstRect.height;
419:
420: int dnumBands = dst.getNumBands();
421: float dstDataArrays[][] = dst.getFloatDataArrays();
422: int dstBandOffsets[] = dst.getBandOffsets();
423: int dstPixelStride = dst.getPixelStride();
424: int dstScanlineStride = dst.getScanlineStride();
425:
426: float srcDataArrays[][] = src.getFloatDataArrays();
427: int bandOffsets[] = src.getBandOffsets();
428:
429: int dstPixelOffset;
430: int dstOffset = 0;
431: int posy, posx, pos;
432:
433: int dstScanlineOffset;
434: // For each band
435: for (int k = 0; k < dnumBands; k++) {
436: float dstData[] = dstDataArrays[k];
437: float srcData[] = srcDataArrays[k];
438: int bandOffset = bandOffsets[k];
439: dstScanlineOffset = dstBandOffsets[k];
440: for (int j = 0; j < dheight; j++) {
441: dstPixelOffset = dstScanlineOffset;
442: posy = yvalues[j] + bandOffset;
443: for (int i = 0; i < dwidth; i++) {
444: posx = xvalues[i];
445: pos = posx + posy;
446: dstData[dstPixelOffset] = srcData[pos];
447: dstPixelOffset += dstPixelStride;
448: }
449: dstScanlineOffset += dstScanlineStride;
450: }
451: }
452: }
453:
454: // identical to byteLoop, except datatypes have changed. clumsy,
455: // but there's no other way in Java
456: private void doubleLoop(RasterAccessor src, Rectangle dstRect,
457: RasterAccessor dst, int xvalues[], int yvalues[]) {
458:
459: int dwidth = dstRect.width;
460: int dheight = dstRect.height;
461:
462: int dnumBands = dst.getNumBands();
463: double dstDataArrays[][] = dst.getDoubleDataArrays();
464: int dstBandOffsets[] = dst.getBandOffsets();
465: int dstPixelStride = dst.getPixelStride();
466: int dstScanlineStride = dst.getScanlineStride();
467:
468: int bandOffsets[] = src.getBandOffsets();
469: double srcDataArrays[][] = src.getDoubleDataArrays();
470:
471: int dstPixelOffset;
472: int dstOffset = 0;
473: int posy, posx, pos;
474:
475: int dstScanlineOffset;
476: // For each band
477: for (int k = 0; k < dnumBands; k++) {
478: double dstData[] = dstDataArrays[k];
479: double srcData[] = srcDataArrays[k];
480: int bandOffset = bandOffsets[k];
481: dstScanlineOffset = dstBandOffsets[k];
482: for (int j = 0; j < dheight; j++) {
483: dstPixelOffset = dstScanlineOffset;
484: posy = yvalues[j] + bandOffset;
485: for (int i = 0; i < dwidth; i++) {
486: posx = xvalues[i];
487: pos = posx + posy;
488: dstData[dstPixelOffset] = srcData[pos];
489: dstPixelOffset += dstPixelStride;
490: }
491: dstScanlineOffset += dstScanlineStride;
492: }
493: }
494: }
495:
496: // public static OpImage createTestImage(OpImageTester oit) {
497: // Interpolation interp =
498: // Interpolation.getInstance(Interpolation.INTERP_NEAREST);
499: // return new ScaleNearestOpImage(oit.getSource(), null,
500: // new ImageLayout(oit.getSource()),
501: // 2.5F, 2.5F, 0.0F, 0.0F,
502: // interp);
503: // }
504:
505: // public static void main(String args[]) {
506:
507: // String classname = "com.sun.media.jai.opimage.ScaleNearestOpImage";
508: // OpImageTester.performDiagnostics(classname, args);
509: // System.exit(1);
510:
511: // System.out.println("ScaleOpImage Test");
512: // ImageLayout layout;
513: // OpImage src, dst;
514: // Rectangle rect = new Rectangle(0, 0, 5, 5);
515:
516: // InterpolationNearest interp = new InterpolationNearest();
517:
518: // System.out.println("1. PixelInterleaved short 3-band");
519: // layout = OpImageTester.createImageLayout(
520: // 0, 0, 200, 200, 0, 0, 64, 64, DataBuffer.TYPE_SHORT, 3, false);
521: // src = OpImageTester.createRandomOpImage(layout);
522: // dst = new ScaleNearestOpImage(src, null, null,
523: // 2.0F, 2.0F, 0.0F, 0.0F, interp);
524: // OpImageTester.testOpImage(dst, rect);
525: // OpImageTester.timeOpImage(dst, 10);
526:
527: // System.out.println("2. PixelInterleaved ushort 3-band");
528: // layout = OpImageTester.createImageLayout(
529: // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_USHORT, 3, false);
530: // src = OpImageTester.createRandomOpImage(layout);
531: // dst = new ScaleNearestOpImage(src, null, null,
532: // 4.0F, 2.0F, 0.0F, 0.0F, interp);
533: // OpImageTester.testOpImage(dst, rect);
534: // OpImageTester.timeOpImage(dst, 10);
535: // }
536: }
|