Source Code Cross Referenced for AffineTransformOp.java in  » 6.0-JDK-Core » AWT » java » awt » image » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » AWT » java.awt.image 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001        /*
002         * Copyright 1997-2005 Sun Microsystems, Inc.  All Rights Reserved.
003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004         *
005         * This code is free software; you can redistribute it and/or modify it
006         * under the terms of the GNU General Public License version 2 only, as
007         * published by the Free Software Foundation.  Sun designates this
008         * particular file as subject to the "Classpath" exception as provided
009         * by Sun in the LICENSE file that accompanied this code.
010         *
011         * This code is distributed in the hope that it will be useful, but WITHOUT
012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014         * version 2 for more details (a copy is included in the LICENSE file that
015         * accompanied this code).
016         *
017         * You should have received a copy of the GNU General Public License version
018         * 2 along with this work; if not, write to the Free Software Foundation,
019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020         *
021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022         * CA 95054 USA or visit www.sun.com if you need additional information or
023         * have any questions.
024         */
025
026        package java.awt.image;
027
028        import java.awt.geom.AffineTransform;
029        import java.awt.geom.NoninvertibleTransformException;
030        import java.awt.geom.Rectangle2D;
031        import java.awt.geom.Point2D;
032        import java.awt.AlphaComposite;
033        import java.awt.GraphicsEnvironment;
034        import java.awt.Rectangle;
035        import java.awt.RenderingHints;
036        import java.awt.Transparency;
037        import sun.awt.image.ImagingLib;
038
039        /**
040         * This class uses an affine transform to perform a linear mapping from
041         * 2D coordinates in the source image or <CODE>Raster</CODE> to 2D coordinates 
042         * in the destination image or <CODE>Raster</CODE>.
043         * The type of interpolation that is used is specified through a constructor, 
044         * either by a <CODE>RenderingHints</CODE> object or by one of the integer 
045         * interpolation types defined in this class.
046         * <p>
047         * If a <CODE>RenderingHints</CODE> object is specified in the constructor, the
048         * interpolation hint and the rendering quality hint are used to set
049         * the interpolation type for this operation.  The color rendering hint
050         * and the dithering hint can be used when color conversion is required.
051         * <p>
052         * Note that the following constraints have to be met:
053         * <ul>
054         * <li>The source and destination must be different.
055         * <li>For <CODE>Raster</CODE> objects, the number of bands in the source must 
056         * be equal to the number of bands in the destination.
057         * </ul>
058         * @see AffineTransform
059         * @see BufferedImageFilter
060         * @see java.awt.RenderingHints#KEY_INTERPOLATION
061         * @see java.awt.RenderingHints#KEY_RENDERING
062         * @see java.awt.RenderingHints#KEY_COLOR_RENDERING
063         * @see java.awt.RenderingHints#KEY_DITHERING
064         * @version 16 Apr 1998
065         */
066        public class AffineTransformOp implements  BufferedImageOp, RasterOp {
067            private AffineTransform xform;
068            RenderingHints hints;
069
070            /**
071             * Nearest-neighbor interpolation type.
072             */
073            public static final int TYPE_NEAREST_NEIGHBOR = 1;
074
075            /**
076             * Bilinear interpolation type.
077             */
078            public static final int TYPE_BILINEAR = 2;
079
080            /**
081             * Bicubic interpolation type.
082             */
083            public static final int TYPE_BICUBIC = 3;
084
085            int interpolationType = TYPE_NEAREST_NEIGHBOR;
086
087            /**
088             * Constructs an <CODE>AffineTransformOp</CODE> given an affine transform.
089             * The interpolation type is determined from the 
090             * <CODE>RenderingHints</CODE> object.  If the interpolation hint is 
091             * defined, it will be used. Otherwise, if the rendering quality hint is
092             * defined, the interpolation type is determined from its value.  If no 
093             * hints are specified (<CODE>hints</CODE> is null),
094             * the interpolation type is {@link #TYPE_NEAREST_NEIGHBOR 
095             * TYPE_NEAREST_NEIGHBOR}.
096             *
097             * @param xform The <CODE>AffineTransform</CODE> to use for the
098             * operation.
099             *
100             * @param hints The <CODE>RenderingHints</CODE> object used to specify
101             * the interpolation type for the operation. 
102             *
103             * @throws ImagingOpException if the transform is non-invertible.
104             * @see java.awt.RenderingHints#KEY_INTERPOLATION
105             * @see java.awt.RenderingHints#KEY_RENDERING
106             */
107            public AffineTransformOp(AffineTransform xform, RenderingHints hints) {
108                validateTransform(xform);
109                this .xform = (AffineTransform) xform.clone();
110                this .hints = hints;
111
112                if (hints != null) {
113                    Object value = hints.get(hints.KEY_INTERPOLATION);
114                    if (value == null) {
115                        value = hints.get(hints.KEY_RENDERING);
116                        if (value == hints.VALUE_RENDER_SPEED) {
117                            interpolationType = TYPE_NEAREST_NEIGHBOR;
118                        } else if (value == hints.VALUE_RENDER_QUALITY) {
119                            interpolationType = TYPE_BILINEAR;
120                        }
121                    } else if (value == hints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) {
122                        interpolationType = TYPE_NEAREST_NEIGHBOR;
123                    } else if (value == hints.VALUE_INTERPOLATION_BILINEAR) {
124                        interpolationType = TYPE_BILINEAR;
125                    } else if (value == hints.VALUE_INTERPOLATION_BICUBIC) {
126                        interpolationType = TYPE_BICUBIC;
127                    }
128                } else {
129                    interpolationType = TYPE_NEAREST_NEIGHBOR;
130                }
131            }
132
133            /**
134             * Constructs an <CODE>AffineTransformOp</CODE> given an affine transform 
135             * and the interpolation type.
136             * 
137             * @param xform The <CODE>AffineTransform</CODE> to use for the operation.
138             * @param interpolationType One of the integer
139             * interpolation type constants defined by this class: 
140             * {@link #TYPE_NEAREST_NEIGHBOR TYPE_NEAREST_NEIGHBOR},
141             * {@link #TYPE_BILINEAR TYPE_BILINEAR},
142             * {@link #TYPE_BICUBIC TYPE_BICUBIC}. 
143             * @throws ImagingOpException if the transform is non-invertible.
144             */
145            public AffineTransformOp(AffineTransform xform,
146                    int interpolationType) {
147                validateTransform(xform);
148                this .xform = (AffineTransform) xform.clone();
149                switch (interpolationType) {
150                case TYPE_NEAREST_NEIGHBOR:
151                case TYPE_BILINEAR:
152                case TYPE_BICUBIC:
153                    break;
154                default:
155                    throw new IllegalArgumentException(
156                            "Unknown interpolation type: " + interpolationType);
157                }
158                this .interpolationType = interpolationType;
159            }
160
161            /**
162             * Returns the interpolation type used by this op.
163             * @return the interpolation type.
164             * @see #TYPE_NEAREST_NEIGHBOR
165             * @see #TYPE_BILINEAR
166             * @see #TYPE_BICUBIC
167             */
168            public final int getInterpolationType() {
169                return interpolationType;
170            }
171
172            /**
173             * Transforms the source <CODE>BufferedImage</CODE> and stores the results 
174             * in the destination <CODE>BufferedImage</CODE>.  
175             * If the color models for the two images do not match, a color
176             * conversion into the destination color model is performed.
177             * If the destination image is null,
178             * a <CODE>BufferedImage</CODE> is created with the source 
179             * <CODE>ColorModel</CODE>.
180             * <p>
181             * The coordinates of the rectangle returned by 
182             * <code>getBounds2D(BufferedImage)</code>
183             * are not necessarily the same as the coordinates of the 
184             * <code>BufferedImage</code> returned by this method.  If the
185             * upper-left corner coordinates of the rectangle are 
186             * negative then this part of the rectangle is not drawn.  If the
187             * upper-left corner coordinates of the  rectangle are positive 
188             * then the filtered image is drawn at that position in the
189             * destination <code>BufferedImage</code>.
190             * <p> 
191             * An <CODE>IllegalArgumentException</CODE> is thrown if the source is
192             * the same as the destination.
193             *
194             * @param src The <CODE>BufferedImage</CODE> to transform.
195             * @param dst The <CODE>BufferedImage</CODE> in which to store the results 
196             * of the transformation.
197             *
198             * @return The filtered <CODE>BufferedImage</CODE>.
199             * @throws IllegalArgumentException if <code>src</code> and 
200             *         <code>dst</code> are the same
201             * @throws ImagingOpException if the image cannot be transformed
202             *         because of a data-processing error that might be 
203             *         caused by an invalid image format, tile format, or
204             *         image-processing operation, or any other unsupported 
205             *         operation.
206             */
207            public final BufferedImage filter(BufferedImage src,
208                    BufferedImage dst) {
209
210                if (src == null) {
211                    throw new NullPointerException("src image is null");
212                }
213                if (src == dst) {
214                    throw new IllegalArgumentException(
215                            "src image cannot be the "
216                                    + "same as the dst image");
217                }
218
219                boolean needToConvert = false;
220                ColorModel srcCM = src.getColorModel();
221                ColorModel dstCM;
222                BufferedImage origDst = dst;
223
224                if (dst == null) {
225                    dst = createCompatibleDestImage(src, null);
226                    dstCM = srcCM;
227                    origDst = dst;
228                } else {
229                    dstCM = dst.getColorModel();
230                    if (srcCM.getColorSpace().getType() != dstCM
231                            .getColorSpace().getType()) {
232                        int type = xform.getType();
233                        boolean needTrans = ((type & (xform.TYPE_MASK_ROTATION | xform.TYPE_GENERAL_TRANSFORM)) != 0);
234                        if (!needTrans && type != xform.TYPE_TRANSLATION
235                                && type != xform.TYPE_IDENTITY) {
236                            double[] mtx = new double[4];
237                            xform.getMatrix(mtx);
238                            // Check out the matrix.  A non-integral scale will force ARGB
239                            // since the edge conditions can't be guaranteed.
240                            needTrans = (mtx[0] != (int) mtx[0] || mtx[3] != (int) mtx[3]);
241                        }
242
243                        if (needTrans
244                                && srcCM.getTransparency() == Transparency.OPAQUE) {
245                            // Need to convert first
246                            ColorConvertOp ccop = new ColorConvertOp(hints);
247                            BufferedImage tmpSrc = null;
248                            int sw = src.getWidth();
249                            int sh = src.getHeight();
250                            if (dstCM.getTransparency() == Transparency.OPAQUE) {
251                                tmpSrc = new BufferedImage(sw, sh,
252                                        BufferedImage.TYPE_INT_ARGB);
253                            } else {
254                                WritableRaster r = dstCM
255                                        .createCompatibleWritableRaster(sw, sh);
256                                tmpSrc = new BufferedImage(dstCM, r, dstCM
257                                        .isAlphaPremultiplied(), null);
258                            }
259                            src = ccop.filter(src, tmpSrc);
260                        } else {
261                            needToConvert = true;
262                            dst = createCompatibleDestImage(src, null);
263                        }
264                    }
265
266                }
267
268                if (interpolationType != TYPE_NEAREST_NEIGHBOR
269                        && dst.getColorModel() instanceof  IndexColorModel) {
270                    dst = new BufferedImage(dst.getWidth(), dst.getHeight(),
271                            BufferedImage.TYPE_INT_ARGB);
272                }
273                if (ImagingLib.filter(this , src, dst) == null) {
274                    throw new ImagingOpException(
275                            "Unable to transform src image");
276                }
277
278                if (needToConvert) {
279                    ColorConvertOp ccop = new ColorConvertOp(hints);
280                    ccop.filter(dst, origDst);
281                } else if (origDst != dst) {
282                    java.awt.Graphics2D g = origDst.createGraphics();
283                    try {
284                        g.setComposite(AlphaComposite.Src);
285                        g.drawImage(dst, 0, 0, null);
286                    } finally {
287                        g.dispose();
288                    }
289                }
290
291                return origDst;
292            }
293
294            /**
295             * Transforms the source <CODE>Raster</CODE> and stores the results in
296             * the destination <CODE>Raster</CODE>.  This operation performs the
297             * transform band by band.
298             * <p>
299             * If the destination <CODE>Raster</CODE> is null, a new 
300             * <CODE>Raster</CODE> is created.
301             * An <CODE>IllegalArgumentException</CODE> may be thrown if the source is
302             * the same as the destination or if the number of bands in
303             * the source is not equal to the number of bands in the
304             * destination.
305             * <p>
306             * The coordinates of the rectangle returned by 
307             * <code>getBounds2D(Raster)</code>
308             * are not necessarily the same as the coordinates of the
309             * <code>WritableRaster</code> returned by this method.  If the
310             * upper-left corner coordinates of rectangle are negative then
311             * this part of the rectangle is not drawn.  If the coordinates 
312             * of the rectangle are positive then the filtered image is drawn at
313             * that position in the destination <code>Raster</code>.
314             * <p>
315             * @param src The <CODE>Raster</CODE> to transform.
316             * @param dst The <CODE>Raster</CODE> in which to store the results of the 
317             * transformation.
318             *
319             * @return The transformed <CODE>Raster</CODE>.
320             *
321             * @throws ImagingOpException if the raster cannot be transformed
322             *         because of a data-processing error that might be
323             *         caused by an invalid image format, tile format, or
324             *         image-processing operation, or any other unsupported
325             *         operation.
326             */
327            public final WritableRaster filter(Raster src, WritableRaster dst) {
328                if (src == null) {
329                    throw new NullPointerException("src image is null");
330                }
331                if (dst == null) {
332                    dst = createCompatibleDestRaster(src);
333                }
334                if (src == dst) {
335                    throw new IllegalArgumentException(
336                            "src image cannot be the "
337                                    + "same as the dst image");
338                }
339                if (src.getNumBands() != dst.getNumBands()) {
340                    throw new IllegalArgumentException("Number of src bands ("
341                            + src.getNumBands() + ") does not match number of "
342                            + " dst bands (" + dst.getNumBands() + ")");
343                }
344
345                if (ImagingLib.filter(this , src, dst) == null) {
346                    throw new ImagingOpException(
347                            "Unable to transform src image");
348                }
349                return dst;
350            }
351
352            /**
353             * Returns the bounding box of the transformed destination.  The
354             * rectangle returned is the actual bounding box of the 
355             * transformed points.  The coordinates of the upper-left corner
356             * of the returned rectangle might not be (0,&nbsp;0).
357             *
358             * @param src The <CODE>BufferedImage</CODE> to be transformed.
359             *
360             * @return The <CODE>Rectangle2D</CODE> representing the destination's
361             * bounding box.
362             */
363            public final Rectangle2D getBounds2D(BufferedImage src) {
364                return getBounds2D(src.getRaster());
365            }
366
367            /**
368             * Returns the bounding box of the transformed destination.  The
369             * rectangle returned will be the actual bounding box of the
370             * transformed points.  The coordinates of the upper-left corner
371             * of the returned rectangle might not be (0,&nbsp;0).
372             *
373             * @param src The <CODE>Raster</CODE> to be transformed.
374             *
375             * @return The <CODE>Rectangle2D</CODE> representing the destination's
376             * bounding box.
377             */
378            public final Rectangle2D getBounds2D(Raster src) {
379                int w = src.getWidth();
380                int h = src.getHeight();
381
382                // Get the bounding box of the src and transform the corners
383                float[] pts = { 0, 0, w, 0, w, h, 0, h };
384                xform.transform(pts, 0, pts, 0, 4);
385
386                // Get the min, max of the dst
387                float fmaxX = pts[0];
388                float fmaxY = pts[1];
389                float fminX = pts[0];
390                float fminY = pts[1];
391                for (int i = 2; i < 8; i += 2) {
392                    if (pts[i] > fmaxX) {
393                        fmaxX = pts[i];
394                    } else if (pts[i] < fminX) {
395                        fminX = pts[i];
396                    }
397                    if (pts[i + 1] > fmaxY) {
398                        fmaxY = pts[i + 1];
399                    } else if (pts[i + 1] < fminY) {
400                        fminY = pts[i + 1];
401                    }
402                }
403
404                return new Rectangle2D.Float(fminX, fminY, fmaxX - fminX, fmaxY
405                        - fminY);
406            }
407
408            /**
409             * Creates a zeroed destination image with the correct size and number of
410             * bands.  A <CODE>RasterFormatException</CODE> may be thrown if the 
411             * transformed width or height is equal to 0.  
412             * <p>
413             * If <CODE>destCM</CODE> is null,
414             * an appropriate <CODE>ColorModel</CODE> is used; this 
415             * <CODE>ColorModel</CODE> may have
416             * an alpha channel even if the source <CODE>ColorModel</CODE> is opaque.
417             *
418             * @param src  The <CODE>BufferedImage</CODE> to be transformed.
419             * @param destCM  <CODE>ColorModel</CODE> of the destination.  If null,
420             * an appropriate <CODE>ColorModel</CODE> is used.  
421             *
422             * @return The zeroed destination image.
423             */
424            public BufferedImage createCompatibleDestImage(BufferedImage src,
425                    ColorModel destCM) {
426                BufferedImage image;
427                Rectangle r = getBounds2D(src).getBounds();
428
429                // If r.x (or r.y) is < 0, then we want to only create an image 
430                // that is in the positive range.
431                // If r.x (or r.y) is > 0, then we need to create an image that
432                // includes the translation.
433                int w = r.x + r.width;
434                int h = r.y + r.height;
435                if (w <= 0) {
436                    throw new RasterFormatException("Transformed width (" + w
437                            + ") is less than or equal to 0.");
438                }
439                if (h <= 0) {
440                    throw new RasterFormatException("Transformed height (" + h
441                            + ") is less than or equal to 0.");
442                }
443
444                if (destCM == null) {
445                    ColorModel cm = src.getColorModel();
446                    if (interpolationType != TYPE_NEAREST_NEIGHBOR
447                            && (cm instanceof  IndexColorModel || cm
448                                    .getTransparency() == Transparency.OPAQUE)) {
449                        image = new BufferedImage(w, h,
450                                BufferedImage.TYPE_INT_ARGB);
451                    } else {
452                        image = new BufferedImage(cm, src.getRaster()
453                                .createCompatibleWritableRaster(w, h), cm
454                                .isAlphaPremultiplied(), null);
455                    }
456                } else {
457                    image = new BufferedImage(destCM, destCM
458                            .createCompatibleWritableRaster(w, h), destCM
459                            .isAlphaPremultiplied(), null);
460                }
461
462                return image;
463            }
464
465            /**
466             * Creates a zeroed destination <CODE>Raster</CODE> with the correct size 
467             * and number of bands.  A <CODE>RasterFormatException</CODE> may be thrown 
468             * if the transformed width or height is equal to 0.
469             *
470             * @param src The <CODE>Raster</CODE> to be transformed.
471             *
472             * @return The zeroed destination <CODE>Raster</CODE>.
473             */
474            public WritableRaster createCompatibleDestRaster(Raster src) {
475                Rectangle2D r = getBounds2D(src);
476
477                return src
478                        .createCompatibleWritableRaster((int) r.getX(), (int) r
479                                .getY(), (int) r.getWidth(), (int) r
480                                .getHeight());
481            }
482
483            /**
484             * Returns the location of the corresponding destination point given a
485             * point in the source.  If <CODE>dstPt</CODE> is specified, it
486             * is used to hold the return value.
487             *
488             * @param srcPt The <code>Point2D</code> that represents the source
489             *              point.
490             * @param dstPt The <CODE>Point2D</CODE> in which to store the result.
491             *
492             * @return The <CODE>Point2D</CODE> in the destination that corresponds to 
493             * the specified point in the source.
494             */
495            public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
496                return xform.transform(srcPt, dstPt);
497            }
498
499            /**
500             * Returns the affine transform used by this transform operation.
501             *
502             * @return The <CODE>AffineTransform</CODE> associated with this op. 
503             */
504            public final AffineTransform getTransform() {
505                return (AffineTransform) xform.clone();
506            }
507
508            /**
509             * Returns the rendering hints used by this transform operation.
510             *
511             * @return The <CODE>RenderingHints</CODE> object associated with this op. 
512             */
513            public final RenderingHints getRenderingHints() {
514                if (hints == null) {
515                    Object val;
516                    switch (interpolationType) {
517                    case TYPE_NEAREST_NEIGHBOR:
518                        val = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
519                        break;
520                    case TYPE_BILINEAR:
521                        val = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
522                        break;
523                    case TYPE_BICUBIC:
524                        val = RenderingHints.VALUE_INTERPOLATION_BICUBIC;
525                        break;
526                    default:
527                        // Should never get here 
528                        throw new InternalError("Unknown interpolation type "
529                                + interpolationType);
530
531                    }
532                    hints = new RenderingHints(
533                            RenderingHints.KEY_INTERPOLATION, val);
534                }
535
536                return hints;
537            }
538
539            // We need to be able to invert the transform if we want to
540            // transform the image.  If the determinant of the matrix is 0,
541            // then we can't invert the transform.
542            void validateTransform(AffineTransform xform) {
543                if (Math.abs(xform.getDeterminant()) <= Double.MIN_VALUE) {
544                    throw new ImagingOpException("Unable to invert transform "
545                            + xform);
546                }
547            }
548        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.