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