1:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50:
51:
61: public class AffineTransformOp implements BufferedImageOp, RasterOp
62: {
63: public static final int TYPE_NEAREST_NEIGHBOR = 1;
64:
65: public static final int TYPE_BILINEAR = 2;
66:
67:
70: public static final int TYPE_BICUBIC = 3;
71:
72: private AffineTransform transform;
73: private RenderingHints hints;
74:
75:
84: public AffineTransformOp (AffineTransform xform, int interpolationType)
85: {
86: this.transform = xform;
87: if (xform.getDeterminant() == 0)
88: throw new ImagingOpException(null);
89:
90: switch (interpolationType)
91: {
92: case TYPE_BILINEAR:
93: hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
94: RenderingHints.VALUE_INTERPOLATION_BILINEAR);
95: break;
96: case TYPE_BICUBIC:
97: hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
98: RenderingHints.VALUE_INTERPOLATION_BICUBIC);
99: break;
100: default:
101: hints = new RenderingHints (RenderingHints.KEY_INTERPOLATION,
102: RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
103: }
104: }
105:
106:
113: public AffineTransformOp (AffineTransform xform, RenderingHints hints)
114: {
115: this.transform = xform;
116: this.hints = hints;
117: if (xform.getDeterminant() == 0)
118: throw new ImagingOpException(null);
119: }
120:
121:
131: public BufferedImage createCompatibleDestImage (BufferedImage src,
132: ColorModel destCM)
133: {
134: if (destCM != null)
135: return new BufferedImage(destCM,
136: createCompatibleDestRaster(src.getRaster()),
137: src.isAlphaPremultiplied(), null);
138:
139:
140:
141: if (src.getType() == BufferedImage.TYPE_INT_ARGB_PRE
142: || src.getType() == BufferedImage.TYPE_4BYTE_ABGR
143: || src.getType() == BufferedImage.TYPE_4BYTE_ABGR_PRE)
144: return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
145:
146: else
147: return new BufferedImage(src.getWidth(), src.getHeight(),
148: BufferedImage.TYPE_INT_ARGB);
149: }
150:
151:
159: public WritableRaster createCompatibleDestRaster (Raster src)
160: {
161: Rectangle2D rect = getBounds2D(src);
162:
163: if (rect.getWidth() == 0 || rect.getHeight() == 0)
164: throw new RasterFormatException("width or height is 0");
165:
166: return src.createCompatibleWritableRaster((int) rect.getWidth(),
167: (int) rect.getHeight());
168: }
169:
170:
181: public final BufferedImage filter (BufferedImage src, BufferedImage dst)
182: {
183: if (dst == src)
184: throw new IllegalArgumentException("src image cannot be the same as "
185: + "the dst image");
186:
187:
188: if (dst == null)
189: dst = createCompatibleDestImage(src, null);
190:
191: Graphics2D gr = dst.createGraphics();
192: gr.setRenderingHints(hints);
193: gr.drawImage(src, transform, null);
194: return dst;
195: }
196:
197:
208: public final WritableRaster filter(Raster src, WritableRaster dst)
209: {
210:
211: if (dst == src)
212: throw new IllegalArgumentException("src image cannot be the same as"
213: + " the dst image");
214:
215: if (dst == null)
216: dst = createCompatibleDestRaster(src);
217:
218: if (src.getNumBands() != dst.getNumBands())
219: throw new IllegalArgumentException("src and dst must have same number"
220: + " of bands");
221:
222:
223:
224: if (ColorModel.getRGBdefault().isCompatibleSampleModel(src.getSampleModel())
225: && ColorModel.getRGBdefault().isCompatibleSampleModel(dst.getSampleModel()))
226: {
227: WritableRaster src2 = Raster.createWritableRaster(src.getSampleModel(),
228: src.getDataBuffer(),
229: new Point(src.getMinX(),
230: src.getMinY()));
231: BufferedImage iSrc = new BufferedImage(ColorModel.getRGBdefault(),
232: src2, false, null);
233: BufferedImage iDst = new BufferedImage(ColorModel.getRGBdefault(), dst,
234: false, null);
235:
236: return filter(iSrc, iDst).getRaster();
237: }
238:
239:
240:
241: double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2];
242: double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2];
243:
244:
245: int i = 0;
246: for (int x = 0; x < dst.getWidth(); x++)
247: {
248: for (int y = 0; y < dst.getHeight(); y++)
249: {
250: dstPts[i++] = x;
251: dstPts[i++] = y;
252: }
253: }
254: Rectangle srcbounds = src.getBounds();
255:
256:
257:
258:
259:
260: try
261: {
262: AffineTransform inverseTx = transform.createInverse();
263: inverseTx.transform(dstPts, 0, srcPts, 0, dstPts.length / 2);
264: }
265: catch (NoninvertibleTransformException e)
266: {
267:
268: throw new ImagingOpException(e.getMessage());
269: }
270:
271:
272: if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
273: filterNearest(src, dst, dstPts, srcPts);
274:
275: else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
276: filterBilinear(src, dst, dstPts, srcPts);
277:
278: else
279: filterBicubic(src, dst, dstPts, srcPts);
280:
281: return dst;
282: }
283:
284:
291: public final Rectangle2D getBounds2D (BufferedImage src)
292: {
293: return getBounds2D (src.getRaster());
294: }
295:
296:
302: public final Rectangle2D getBounds2D (Raster src)
303: {
304: return transform.createTransformedShape(src.getBounds()).getBounds2D();
305: }
306:
307:
312: public final int getInterpolationType ()
313: {
314: if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
315: return TYPE_BILINEAR;
316:
317: else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC))
318: return TYPE_BICUBIC;
319:
320: else
321: return TYPE_NEAREST_NEIGHBOR;
322: }
323:
324:
332: public final Point2D getPoint2D (Point2D srcPt, Point2D dstPt)
333: {
334: return transform.transform (srcPt, dstPt);
335: }
336:
337:
342: public final RenderingHints getRenderingHints ()
343: {
344: return hints;
345: }
346:
347:
353: public final AffineTransform getTransform ()
354: {
355: return transform;
356: }
357:
358:
366: private void filterNearest(Raster src, WritableRaster dst, double[] dpts,
367: double[] pts)
368: {
369: Rectangle srcbounds = src.getBounds();
370:
371:
372:
373: for (int i = 0; i < dpts.length; i += 2)
374: {
375: int srcX = (int) Math.round(pts[i]) + src.getMinX();
376: int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
377:
378: if (srcbounds.contains(srcX, srcY))
379: dst.setDataElements((int) dpts[i] + dst.getMinX(),
380: (int) dpts[i + 1] + dst.getMinY(),
381: src.getDataElements(srcX, srcY, null));
382: }
383: }
384:
385:
393: private void filterBilinear(Raster src, WritableRaster dst, double[] dpts,
394: double[] pts)
395: {
396: Rectangle srcbounds = src.getBounds();
397:
398: Object xyarr = null;
399: Object xp1arr = null;
400: Object yp1arr = null;
401: Object xyp1arr = null;
402:
403: double xy;
404: double xp1;
405: double yp1;
406: double xyp1;
407:
408: double[] result = new double[src.getNumBands()];
409:
410:
411:
412: for (int i = 0; i < dpts.length; i += 2)
413: {
414: int srcX = (int) Math.round(pts[i]) + src.getMinX();
415: int srcY = (int) Math.round(pts[i + 1]) + src.getMinY();
416:
417: if (srcbounds.contains(srcX, srcY))
418: {
419:
420: if (pts[i] >= src.getWidth() - 1
421: || pts[i + 1] >= src.getHeight() - 1)
422: dst.setDataElements((int) dpts[i] + dst.getMinX(),
423: (int) dpts[i + 1] + dst.getMinY(),
424: src.getDataElements(srcX, srcY, null));
425:
426:
427: else
428: {
429: int x = (int) Math.floor(pts[i] + src.getMinX());
430: int y = (int) Math.floor(pts[i + 1] + src.getMinY());
431: double xdiff = pts[i] + src.getMinX() - x;
432: double ydiff = pts[i + 1] + src.getMinY() - y;
433:
434:
435:
436: if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
437: || src.getTransferType() == DataBuffer.TYPE_FLOAT)
438: {
439: xyarr = src.getPixel(x, y, (double[])xyarr);
440: xp1arr = src.getPixel(x+1, y, (double[])xp1arr);
441: yp1arr = src.getPixel(x, y+1, (double[])yp1arr);
442: xyp1arr = src.getPixel(x+1, y+1, (double[])xyp1arr);
443: }
444: else
445: {
446: xyarr = src.getPixel(x, y, (int[])xyarr);
447: xp1arr = src.getPixel(x+1, y, (int[])xp1arr);
448: yp1arr = src.getPixel(x, y+1, (int[])yp1arr);
449: xyp1arr = src.getPixel(x+1, y+1, (int[])xyp1arr);
450: }
451:
452:
453:
454:
455:
456:
457: for (int j = 0; j < src.getNumBands(); j++)
458: {
459:
460: if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
461: || src.getTransferType() == DataBuffer.TYPE_FLOAT)
462: {
463: xy = ((double[])xyarr)[j];
464: xp1 = ((double[])xp1arr)[j];
465: yp1 = ((double[])yp1arr)[j];
466: xyp1 = ((double[])xyp1arr)[j];
467: }
468: else
469: {
470: xy = ((int[])xyarr)[j];
471: xp1 = ((int[])xp1arr)[j];
472: yp1 = ((int[])yp1arr)[j];
473: xyp1 = ((int[])xyp1arr)[j];
474: }
475:
476:
477:
478: if (xy == xp1 && xy == yp1 && xy == xyp1)
479: result[j] = xy;
480:
481:
482: else
483: result[j] = (xy * (1-xdiff) + xp1 * xdiff)
484: * (1-ydiff)
485: + (yp1 * (1-xdiff) + xyp1 * xdiff)
486: * ydiff;
487: }
488:
489: dst.setPixel((int)dpts[i] + dst.getMinX(),
490: (int)dpts[i+1] + dst.getMinY(),
491: result);
492: }
493: }
494: }
495: }
496:
497:
506: private void filterBicubic(Raster src, WritableRaster dst, double[] dpts,
507: double[] pts)
508: {
509: Rectangle srcbounds = src.getBounds();
510: double[] result = new double[src.getNumBands()];
511: Object pixels = null;
512:
513:
514:
515: for (int i = 0; i < dpts.length; i += 2)
516: {
517: if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(),
518: (int) Math.round(pts[i + 1]) + src.getMinY()))
519: {
520: int x = (int) Math.floor(pts[i] + src.getMinX());
521: int y = (int) Math.floor(pts[i + 1] + src.getMinY());
522: double dx = pts[i] + src.getMinX() - x;
523: double dy = pts[i + 1] + src.getMinY() - y;
524: Arrays.fill(result, 0);
525:
526: for (int m = - 1; m < 3; m++)
527: for (int n = - 1; n < 3; n++)
528: {
529:
530: double r1 = 0;
531: double r2 = 0;
532:
533:
534: double rx = m - dx + 2;
535: r1 += rx * rx * rx;
536:
537: rx = m - dx + 1;
538: if (rx > 0)
539: r1 -= 4 * rx * rx * rx;
540:
541: rx = m - dx;
542: if (rx > 0)
543: r1 += 6 * rx * rx * rx;
544:
545: rx = m - dx - 1;
546: if (rx > 0)
547: r1 -= 4 * rx * rx * rx;
548:
549: r1 /= 6;
550:
551:
552: rx = dy - n + 2;
553: if (rx > 0)
554: r2 += rx * rx * rx;
555:
556: rx = dy - n + 1;
557: if (rx > 0)
558: r2 -= 4 * rx * rx * rx;
559:
560: rx = dy - n;
561: if (rx > 0)
562: r2 += 6 * rx * rx * rx;
563:
564: rx = dy - n - 1;
565: if (rx > 0)
566: r2 -= 4 * rx * rx * rx;
567:
568: r2 /= 6;
569:
570:
571:
572: int srcX = x + m;
573: if (srcX >= src.getMinX() + src.getWidth())
574: srcX = src.getMinX() + src.getWidth() - 1;
575: else if (srcX < src.getMinX())
576: srcX = src.getMinX();
577:
578: int srcY = y + n;
579: if (srcY >= src.getMinY() + src.getHeight())
580: srcY = src.getMinY() + src.getHeight() - 1;
581: else if (srcY < src.getMinY())
582: srcY = src.getMinY();
583:
584:
585:
586: if (src.getTransferType() == DataBuffer.TYPE_DOUBLE
587: || src.getTransferType() == DataBuffer.TYPE_FLOAT)
588: {
589: pixels = src.getPixel(srcX, srcY, (double[])pixels);
590: for (int j = 0; j < result.length; j++)
591: result[j] += ((double[])pixels)[j] * r1 * r2;
592: }
593: else
594: {
595: pixels = src.getPixel(srcX, srcY, (int[])pixels);
596: for (int j = 0; j < result.length; j++)
597: result[j] += ((int[])pixels)[j] * r1 * r2;
598: }
599: }
600:
601:
602: dst.setPixel((int)dpts[i] + dst.getMinX(),
603: (int)dpts[i+1] + dst.getMinY(),
604: result);
605: }
606: }
607: }
608: }