001: /*
002: * $RCSfile: AffineCRIF.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:13 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.RenderingHints;
015: import java.awt.geom.AffineTransform;
016: import java.awt.image.DataBuffer;
017: import java.awt.image.MultiPixelPackedSampleModel;
018: import java.awt.image.RenderedImage;
019: import java.awt.image.SampleModel;
020: import java.awt.image.renderable.RenderableImage;
021: import java.awt.image.renderable.RenderableImageOp;
022: import java.awt.image.renderable.RenderContext;
023: import java.awt.image.renderable.ParameterBlock;
024: import java.awt.image.renderable.RenderedImageFactory;
025: import javax.media.jai.BorderExtender;
026: import javax.media.jai.ImageLayout;
027: import javax.media.jai.Interpolation;
028: import javax.media.jai.InterpolationBicubic;
029: import javax.media.jai.InterpolationBicubic2;
030: import javax.media.jai.InterpolationBilinear;
031: import javax.media.jai.InterpolationNearest;
032: import javax.media.jai.InterpolationTable;
033: import javax.media.jai.PlanarImage;
034: import javax.media.jai.RenderedOp;
035: import javax.media.jai.TileCache;
036: import javax.media.jai.CRIFImpl;
037: import java.util.Map;
038: import java.awt.geom.Rectangle2D;
039: import java.awt.geom.Point2D;
040:
041: /**
042: * @since EA4
043: * @see AffineOpimage, ScaleOpImage
044: */
045: public class AffineCRIF extends CRIFImpl {
046:
047: private static final float TOLERANCE = 0.01F;
048:
049: /** Constructor. */
050: public AffineCRIF() {
051: super ("affine");
052: }
053:
054: /**
055: * Creates an affine operation as an instance of AffineOpImage.
056: */
057: public RenderedImage create(ParameterBlock paramBlock,
058: RenderingHints renderHints) {
059: // Get ImageLayout from renderHints if any.
060: ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints);
061:
062: // Get TileCache from renderHints if any.
063: TileCache cache = RIFUtil.getTileCacheHint(renderHints);
064:
065: // Get BorderExtender from renderHints if any.
066: BorderExtender extender = RIFUtil
067: .getBorderExtenderHint(renderHints);
068:
069: RenderedImage source = paramBlock.getRenderedSource(0);
070:
071: Object arg0 = paramBlock.getObjectParameter(0);
072: AffineTransform transform = (AffineTransform) arg0;
073:
074: Object arg1 = paramBlock.getObjectParameter(1);
075: Interpolation interp = (Interpolation) arg1;
076:
077: double[] backgroundValues = (double[]) paramBlock
078: .getObjectParameter(2);
079:
080: SampleModel sm = source.getSampleModel();
081: boolean isBinary = (sm instanceof MultiPixelPackedSampleModel)
082: && (sm.getSampleSize(0) == 1)
083: && (sm.getDataType() == DataBuffer.TYPE_BYTE
084: || sm.getDataType() == DataBuffer.TYPE_USHORT || sm
085: .getDataType() == DataBuffer.TYPE_INT);
086:
087: // Get the affine transform
088: double tr[];
089: tr = new double[6];
090: transform.getMatrix(tr);
091:
092: //
093: // Check and see if the affine transform is doing a copy.
094: // If so call the copy operation.
095: //
096: if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0)
097: && (tr[1] == 0.0) && (tr[4] == 0.0) && (tr[5] == 0.0)) {
098: // It's a copy
099: return new CopyOpImage(source, renderHints, layout);
100: }
101:
102: //
103: // Check and see if the affine transform is in fact doing
104: // a Translate operation. That is a scale by 1 and no rotation.
105: // In which case call translate. Note that only integer translate
106: // is applicable. For non-integer translate we'll have to do the
107: // affine.
108: // If the hints contain an ImageLayout hint, we can't use
109: // TranslateIntOpImage since it isn't capable of dealing with that.
110: if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0)
111: && (tr[1] == 0.0)
112: && (Math.abs(tr[4] - (int) tr[4]) < TOLERANCE)
113: && (Math.abs(tr[5] - (int) tr[5]) < TOLERANCE)
114: && layout == null) {
115: // It's a integer translate
116: return new TranslateIntOpImage(source, renderHints,
117: (int) tr[4], (int) tr[5]);
118: }
119:
120: //
121: // Check and see if the affine transform is in fact doing
122: // a Scale operation. In which case call Scale which is more
123: // optimized than Affine.
124: //
125: if ((tr[0] > 0.0) && (tr[2] == 0.0) && (tr[1] == 0.0)
126: && (tr[3] > 0.0)) {
127: // It's a scale
128: if (interp instanceof InterpolationNearest) {
129: if (isBinary) {
130: return new ScaleNearestBinaryOpImage(source,
131: extender, renderHints, layout,
132: (float) tr[0], (float) tr[3],
133: (float) tr[4], (float) tr[5], interp);
134: } else {
135: return new ScaleNearestOpImage(source, extender,
136: renderHints, layout, (float) tr[0], // xScale
137: (float) tr[3], // yScale
138: (float) tr[4], // xTrans
139: (float) tr[5], // yTrans
140: interp);
141: }
142: } else if (interp instanceof InterpolationBilinear) {
143: if (isBinary) {
144: return new ScaleBilinearBinaryOpImage(source,
145: extender, renderHints, layout,
146: (float) tr[0], (float) tr[3],
147: (float) tr[4], (float) tr[5], interp);
148: } else {
149:
150: return new ScaleBilinearOpImage(source, extender,
151: renderHints, layout, (float) tr[0], // xScale
152: (float) tr[3], // yScale
153: (float) tr[4], // xTrans
154: (float) tr[5], // yTrans
155: interp);
156: }
157: } else if ((interp instanceof InterpolationBicubic)
158: || (interp instanceof InterpolationBicubic2)) {
159: return new ScaleBicubicOpImage(source, extender,
160: renderHints, layout, (float) tr[0], // xScale
161: (float) tr[3], // yScale
162: (float) tr[4], // xTrans
163: (float) tr[5], // yTrans
164: interp);
165: } else {
166: return new ScaleGeneralOpImage(source, extender,
167: renderHints, layout, (float) tr[0], // xScale
168: (float) tr[3], // yScale
169: (float) tr[4], // xTrans
170: (float) tr[5], // yTrans
171: interp);
172: }
173: }
174:
175: // Have to do Affine
176: if (interp instanceof InterpolationNearest) {
177: if (isBinary) {
178: return new AffineNearestBinaryOpImage(source, extender,
179: renderHints, layout, transform, interp,
180: backgroundValues);
181: } else {
182: return new AffineNearestOpImage(source, extender,
183: renderHints, layout, transform, interp,
184: backgroundValues);
185: }
186: } else if (interp instanceof InterpolationBilinear) {
187: return new AffineBilinearOpImage(source, extender,
188: renderHints, layout, transform, interp,
189: backgroundValues);
190: } else if (interp instanceof InterpolationBicubic) {
191: return new AffineBicubicOpImage(source, extender,
192: renderHints, layout, transform, interp,
193: backgroundValues);
194: } else if (interp instanceof InterpolationBicubic2) {
195: return new AffineBicubic2OpImage(source, extender,
196: renderHints, layout, transform, interp,
197: backgroundValues);
198: } else {
199: return new AffineGeneralOpImage(source, extender,
200: renderHints, layout, transform, interp,
201: backgroundValues);
202: }
203:
204: }
205:
206: /**
207: * Creates a new instance of <code>AffineOpImage</code>
208: * in the renderable layer. This method satisfies the
209: * implementation of CRIF.
210: */
211: public RenderedImage create(RenderContext renderContext,
212: ParameterBlock paramBlock) {
213: return paramBlock.getRenderedSource(0);
214: }
215:
216: /**
217: * Maps the output RenderContext into the RenderContext for the ith
218: * source.
219: * This method satisfies the implementation of CRIF.
220: *
221: * @param i The index of the source image.
222: * @param renderContext The renderContext being applied to the operation.
223: * @param paramBlock The ParameterBlock containing the sources
224: * and the translation factors.
225: * @param image The RenderableImageOp from which this method
226: * was called.
227: */
228: public RenderContext mapRenderContext(int i,
229: RenderContext renderContext, ParameterBlock paramBlock,
230: RenderableImage image) {
231: Object arg0 = paramBlock.getObjectParameter(0);
232: AffineTransform affine = (AffineTransform) arg0;
233:
234: RenderContext RC = (RenderContext) renderContext.clone();
235: AffineTransform usr2dev = RC.getTransform();
236: usr2dev.concatenate(affine);
237: RC.setTransform(usr2dev);
238: return RC;
239: }
240:
241: /**
242: * Gets the bounding box for the output of <code>AffineOpImage</code>.
243: * This method satisfies the implementation of CRIF.
244: */
245: public Rectangle2D getBounds2D(ParameterBlock paramBlock) {
246: RenderableImage source = paramBlock.getRenderableSource(0);
247: Object arg0 = paramBlock.getObjectParameter(0);
248: AffineTransform forward_tr = (AffineTransform) arg0;
249:
250: Object arg1 = paramBlock.getObjectParameter(1);
251: Interpolation interp = (Interpolation) arg1;
252:
253: // Get the affine transform
254: double tr[];
255: tr = new double[6];
256: forward_tr.getMatrix(tr);
257:
258: //
259: // Check and see if the affine transform is doing a copy.
260: //
261: if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0)
262: && (tr[1] == 0.0) && (tr[4] == 0.0) && (tr[5] == 0.0)) {
263: return new Rectangle2D.Float(source.getMinX(), source
264: .getMinY(), source.getWidth(), source.getHeight());
265: }
266:
267: //
268: // Check and see if the affine transform is in fact doing
269: // a Translate operation.
270: //
271: if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0)
272: && (tr[1] == 0.0)
273: && (Math.abs(tr[4] - (int) tr[4]) < TOLERANCE)
274: && (Math.abs(tr[5] - (int) tr[5]) < TOLERANCE)) {
275: return new Rectangle2D.Float(source.getMinX()
276: + (float) tr[4], source.getMinY() + (float) tr[5],
277: source.getWidth(), source.getHeight());
278: }
279:
280: //
281: // Check and see if the affine transform is in fact doing
282: // a Scale operation.
283: //
284: if ((tr[0] > 0.0) && (tr[2] == 0.0) && (tr[1] == 0.0)
285: && (tr[3] > 0.0)) {
286: // Get the source dimensions
287: float x0 = (float) source.getMinX();
288: float y0 = (float) source.getMinY();
289: float w = (float) source.getWidth();
290: float h = (float) source.getHeight();
291:
292: // Forward map the source using x0, y0, w and h
293: float d_x0 = x0 * (float) tr[0] + (float) tr[4];
294: float d_y0 = y0 * (float) tr[3] + (float) tr[5];
295: float d_w = w * (float) tr[0];
296: float d_h = h * (float) tr[3];
297:
298: return new Rectangle2D.Float(d_x0, d_y0, d_w, d_h);
299: }
300:
301: // It's an Affine
302:
303: //
304: // Get sx0,sy0 coordinates and width & height of the source
305: //
306: float sx0 = (float) source.getMinX();
307: float sy0 = (float) source.getMinY();
308: float sw = (float) source.getWidth();
309: float sh = (float) source.getHeight();
310:
311: //
312: // The 4 points (clockwise order) are
313: // (sx0, sy0), (sx0+sw, sy0)
314: // (sx0, sy0+sh), (sx0+sw, sy0+sh)
315: //
316: Point2D[] pts = new Point2D[4];
317: pts[0] = new Point2D.Float(sx0, sy0);
318: pts[1] = new Point2D.Float((sx0 + sw), sy0);
319: pts[2] = new Point2D.Float((sx0 + sw), (sy0 + sh));
320: pts[3] = new Point2D.Float(sx0, (sy0 + sh));
321:
322: // Forward map
323: forward_tr.transform(pts, 0, pts, 0, 4);
324:
325: float dx0 = Float.MAX_VALUE;
326: float dy0 = Float.MAX_VALUE;
327: float dx1 = -Float.MAX_VALUE;
328: float dy1 = -Float.MAX_VALUE;
329: for (int i = 0; i < 4; i++) {
330: float px = (float) pts[i].getX();
331: float py = (float) pts[i].getY();
332:
333: dx0 = Math.min(dx0, px);
334: dy0 = Math.min(dy0, py);
335: dx1 = Math.max(dx1, px);
336: dy1 = Math.max(dy1, py);
337: }
338:
339: //
340: // Get the width & height of the resulting bounding box.
341: // This is set on the layout
342: //
343: float lw = dx1 - dx0;
344: float lh = dy1 - dy0;
345:
346: return new Rectangle2D.Float(dx0, dy0, lw, lh);
347: }
348: }
|