Source Code Cross Referenced for SunGraphics2D.java in  » JDK-Modules-sun » java2d » sun » java2d » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. JDK Core
2. JDK Modules
3. JDK Modules com.sun
4. JDK Modules com.sun.java
5. JDK Modules Platform
6. JDK Modules sun
7. Open Source Build
8. Open Source Graphic Library
9. Open Source IDE Eclipse
10. Open Source J2EE
11. Open Source JDBC Driver
12. Open Source Library
13. Open Source Library Database
14. Open Source Net
15. Open Source Script
16. Science
17. Security
18. Sevlet Container
19. SUN GlassFish
20. Swing Library
21. Web Services apache cxf 2.0.1
22. Web Services AXIS2
23. XML
Microsoft Office Word 2007 Tutorial
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
C# / C Sharp
C# / CSharp Tutorial
ASP.Net
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
PHP
Python
SQL Server / T-SQL
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Java Source Code / Java Documentation » JDK Modules sun » java2d » sun.java2d 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 1996-2007 Sun Microsystems, Inc.  All Rights Reserved.
0003:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004:         *
0005:         * This code is free software; you can redistribute it and/or modify it
0006:         * under the terms of the GNU General Public License version 2 only, as
0007:         * published by the Free Software Foundation.  Sun designates this
0008:         * particular file as subject to the "Classpath" exception as provided
0009:         * by Sun in the LICENSE file that accompanied this code.
0010:         *
0011:         * This code is distributed in the hope that it will be useful, but WITHOUT
0012:         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013:         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014:         * version 2 for more details (a copy is included in the LICENSE file that
0015:         * accompanied this code).
0016:         *
0017:         * You should have received a copy of the GNU General Public License version
0018:         * 2 along with this work; if not, write to the Free Software Foundation,
0019:         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020:         *
0021:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022:         * CA 95054 USA or visit www.sun.com if you need additional information or
0023:         * have any questions.
0024:         */
0025:
0026:        package sun.java2d;
0027:
0028:        import java.awt.Graphics;
0029:        import java.awt.Graphics2D;
0030:        import java.awt.RenderingHints;
0031:        import java.awt.RenderingHints.Key;
0032:        import java.awt.geom.Area;
0033:        import java.awt.geom.AffineTransform;
0034:        import java.awt.geom.NoninvertibleTransformException;
0035:        import java.awt.AlphaComposite;
0036:        import java.awt.BasicStroke;
0037:        import java.awt.image.BufferedImage;
0038:        import java.awt.image.BufferedImageOp;
0039:        import java.awt.image.RenderedImage;
0040:        import java.awt.image.renderable.RenderableImage;
0041:        import java.awt.image.renderable.RenderContext;
0042:        import java.awt.image.AffineTransformOp;
0043:        import java.awt.image.Raster;
0044:        import java.awt.image.SampleModel;
0045:        import java.awt.image.VolatileImage;
0046:        import java.awt.image.WritableRaster;
0047:        import java.awt.Image;
0048:        import java.awt.Composite;
0049:        import java.awt.Color;
0050:        import java.awt.color.ColorSpace;
0051:        import java.awt.image.DataBuffer;
0052:        import java.awt.image.ColorModel;
0053:        import java.awt.image.IndexColorModel;
0054:        import java.awt.image.DirectColorModel;
0055:        import java.awt.GraphicsConfiguration;
0056:        import java.awt.Paint;
0057:        import java.awt.GradientPaint;
0058:        import java.awt.LinearGradientPaint;
0059:        import java.awt.RadialGradientPaint;
0060:        import java.awt.TexturePaint;
0061:        import java.awt.geom.Point2D;
0062:        import java.awt.geom.Rectangle2D;
0063:        import java.awt.geom.PathIterator;
0064:        import java.awt.geom.GeneralPath;
0065:        import java.awt.Shape;
0066:        import java.awt.Stroke;
0067:        import java.awt.FontMetrics;
0068:        import java.awt.Rectangle;
0069:        import java.text.AttributedCharacterIterator;
0070:        import java.awt.Font;
0071:        import java.awt.image.ImageObserver;
0072:        import java.awt.image.ColorConvertOp;
0073:        import java.awt.Transparency;
0074:        import java.awt.font.GlyphVector;
0075:        import java.awt.font.TextLayout;
0076:        import sun.font.FontDesignMetrics;
0077:        import sun.font.StandardGlyphVector;
0078:        import sun.java2d.pipe.PixelDrawPipe;
0079:        import sun.java2d.pipe.PixelFillPipe;
0080:        import sun.java2d.pipe.ShapeDrawPipe;
0081:        import sun.java2d.pipe.ValidatePipe;
0082:        import sun.java2d.pipe.ShapeSpanIterator;
0083:        import sun.java2d.pipe.Region;
0084:        import sun.java2d.pipe.RegionIterator;
0085:        import sun.java2d.pipe.TextPipe;
0086:        import sun.java2d.pipe.DrawImagePipe;
0087:        import sun.java2d.loops.FontInfo;
0088:        import sun.java2d.loops.RenderLoops;
0089:        import sun.java2d.loops.CompositeType;
0090:        import sun.java2d.loops.SurfaceType;
0091:        import sun.java2d.loops.Blit;
0092:        import sun.java2d.loops.BlitBg;
0093:        import sun.java2d.loops.MaskFill;
0094:        import sun.font.FontManager;
0095:        import java.awt.font.FontRenderContext;
0096:        import sun.java2d.loops.XORComposite;
0097:        import sun.awt.ConstrainableGraphics;
0098:        import sun.awt.SunHints;
0099:        import java.util.Map;
0100:        import java.util.Iterator;
0101:        import sun.awt.image.OffScreenImage;
0102:        import sun.misc.PerformanceLogger;
0103:
0104:        /**
0105:         * This is a the master Graphics2D superclass for all of the Sun
0106:         * Graphics implementations.  This class relies on subclasses to
0107:         * manage the various device information, but provides an overall
0108:         * general framework for performing all of the requests in the
0109:         * Graphics and Graphics2D APIs.
0110:         *
0111:         * @version 1.211 05/07/98
0112:         * @author Jim Graham
0113:         */
0114:        public final class SunGraphics2D extends Graphics2D implements 
0115:                ConstrainableGraphics, Cloneable {
0116:            /*
0117:             * Attribute States
0118:             */
0119:            /* Paint */
0120:            public static final int PAINT_CUSTOM = 6; /* Any other Paint object */
0121:            public static final int PAINT_TEXTURE = 5; /* Tiled Image */
0122:            public static final int PAINT_RAD_GRADIENT = 4; /* Color RadialGradient */
0123:            public static final int PAINT_LIN_GRADIENT = 3; /* Color LinearGradient */
0124:            public static final int PAINT_GRADIENT = 2; /* Color Gradient */
0125:            public static final int PAINT_ALPHACOLOR = 1; /* Non-opaque Color */
0126:            public static final int PAINT_OPAQUECOLOR = 0; /* Opaque Color */
0127:
0128:            /* Composite*/
0129:            public static final int COMP_CUSTOM = 3;/* Custom Composite */
0130:            public static final int COMP_XOR = 2;/* XOR Mode Composite */
0131:            public static final int COMP_ALPHA = 1;/* AlphaComposite */
0132:            public static final int COMP_ISCOPY = 0;/* simple stores into destination,
0133:             * i.e. Src, SrcOverNoEa, and other
0134:             * alpha modes which replace
0135:             * the destination.
0136:             */
0137:
0138:            /* Stroke */
0139:            public static final int STROKE_CUSTOM = 3; /* custom Stroke */
0140:            public static final int STROKE_WIDE = 2; /* BasicStroke */
0141:            public static final int STROKE_THINDASHED = 1; /* BasicStroke */
0142:            public static final int STROKE_THIN = 0; /* BasicStroke */
0143:
0144:            /* Transform */
0145:            public static final int TRANSFORM_GENERIC = 4; /* any 3x2 */
0146:            public static final int TRANSFORM_TRANSLATESCALE = 3; /* scale XY */
0147:            public static final int TRANSFORM_ANY_TRANSLATE = 2; /* non-int translate */
0148:            public static final int TRANSFORM_INT_TRANSLATE = 1; /* int translate */
0149:            public static final int TRANSFORM_ISIDENT = 0; /* Identity */
0150:
0151:            /* Clipping */
0152:            public static final int CLIP_SHAPE = 2; /* arbitrary clip */
0153:            public static final int CLIP_RECTANGULAR = 1; /* rectangular clip */
0154:            public static final int CLIP_DEVICE = 0; /* no clipping set */
0155:
0156:            /* The following fields are used when the current Paint is a Color. */
0157:            public int eargb; // ARGB value with ExtraAlpha baked in
0158:            public int pixel; // pixel value for eargb
0159:
0160:            public SurfaceData surfaceData;
0161:
0162:            public PixelDrawPipe drawpipe;
0163:            public PixelFillPipe fillpipe;
0164:            public DrawImagePipe imagepipe;
0165:            public ShapeDrawPipe shapepipe;
0166:            public TextPipe textpipe;
0167:            public MaskFill alphafill;
0168:
0169:            public RenderLoops loops;
0170:
0171:            public CompositeType imageComp; /* Image Transparency checked on fly */
0172:
0173:            public int paintState;
0174:            public int compositeState;
0175:            public int strokeState;
0176:            public int transformState;
0177:            public int clipState;
0178:
0179:            public Color foregroundColor;
0180:            public Color backgroundColor;
0181:
0182:            public AffineTransform transform;
0183:            public int transX;
0184:            public int transY;
0185:
0186:            protected static final Stroke defaultStroke = new BasicStroke();
0187:            protected static final Composite defaultComposite = AlphaComposite.SrcOver;
0188:            private static final Font defaultFont = new Font(Font.DIALOG,
0189:                    Font.PLAIN, 12);
0190:
0191:            public Paint paint;
0192:            public Stroke stroke;
0193:            public Composite composite;
0194:            protected Font font;
0195:            protected FontMetrics fontMetrics;
0196:
0197:            public int renderHint;
0198:            public int antialiasHint;
0199:            public int textAntialiasHint;
0200:            private int fractionalMetricsHint;
0201:
0202:            /* A gamma adjustment to the colour used in lcd text blitting */
0203:            public int lcdTextContrast;
0204:            private static int lcdTextContrastDefaultValue = 140;
0205:
0206:            private int interpolationHint; // raw value of rendering Hint
0207:            public int strokeHint;
0208:
0209:            public int interpolationType; // algorithm choice based on
0210:            // interpolation and render Hints
0211:
0212:            public RenderingHints hints;
0213:
0214:            public Region constrainClip; // lightweight bounds
0215:            public int constrainX;
0216:            public int constrainY;
0217:
0218:            public Region clipRegion;
0219:            public Shape usrClip;
0220:            protected Region devClip; // Actual physical drawable
0221:
0222:            // cached state for text rendering
0223:            private boolean validFontInfo;
0224:            private FontInfo fontInfo;
0225:            private FontInfo glyphVectorFontInfo;
0226:            private FontRenderContext glyphVectorFRC;
0227:
0228:            private final static int slowTextTransformMask = AffineTransform.TYPE_GENERAL_TRANSFORM
0229:                    | AffineTransform.TYPE_MASK_ROTATION
0230:                    | AffineTransform.TYPE_FLIP;
0231:
0232:            static {
0233:                if (PerformanceLogger.loggingEnabled()) {
0234:                    PerformanceLogger
0235:                            .setTime("SunGraphics2D static initialization");
0236:                }
0237:            }
0238:
0239:            public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) {
0240:                surfaceData = sd;
0241:                foregroundColor = fg;
0242:                backgroundColor = bg;
0243:
0244:                transform = new AffineTransform();
0245:                stroke = defaultStroke;
0246:                composite = defaultComposite;
0247:                paint = foregroundColor;
0248:
0249:                imageComp = CompositeType.SrcOverNoEa;
0250:
0251:                renderHint = SunHints.INTVAL_RENDER_DEFAULT;
0252:                antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
0253:                textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
0254:                fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
0255:                lcdTextContrast = lcdTextContrastDefaultValue;
0256:                interpolationHint = -1;
0257:                strokeHint = SunHints.INTVAL_STROKE_DEFAULT;
0258:
0259:                interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
0260:
0261:                validateColor();
0262:
0263:                font = f;
0264:                if (font == null) {
0265:                    font = defaultFont;
0266:                }
0267:
0268:                loops = sd.getRenderLoops(this );
0269:                setDevClip(sd.getBounds());
0270:                invalidatePipe();
0271:            }
0272:
0273:            protected Object clone() {
0274:                try {
0275:                    SunGraphics2D g = (SunGraphics2D) super .clone();
0276:                    g.transform = new AffineTransform(this .transform);
0277:                    if (hints != null) {
0278:                        g.hints = (RenderingHints) this .hints.clone();
0279:                    }
0280:                    /* FontInfos are re-used, so must be cloned too, if they
0281:                     * are valid, and be nulled out if invalid.
0282:                     * The implied trade-off is that there is more to be gained
0283:                     * from re-using these objects than is lost by having to
0284:                     * clone them when the SG2D is cloned.
0285:                     */
0286:                    if (this .fontInfo != null) {
0287:                        if (this .validFontInfo) {
0288:                            g.fontInfo = (FontInfo) this .fontInfo.clone();
0289:                        } else {
0290:                            g.fontInfo = null;
0291:                        }
0292:                    }
0293:                    if (this .glyphVectorFontInfo != null) {
0294:                        g.glyphVectorFontInfo = (FontInfo) this .glyphVectorFontInfo
0295:                                .clone();
0296:                        g.glyphVectorFRC = this .glyphVectorFRC;
0297:                    }
0298:                    //g.invalidatePipe();
0299:                    return g;
0300:                } catch (CloneNotSupportedException e) {
0301:                }
0302:                return null;
0303:            }
0304:
0305:            /**
0306:             * Create a new SunGraphics2D based on this one.
0307:             */
0308:            public Graphics create() {
0309:                return (Graphics) clone();
0310:            }
0311:
0312:            public void setDevClip(int x, int y, int w, int h) {
0313:                Region c = constrainClip;
0314:                if (c == null) {
0315:                    devClip = Region.getInstanceXYWH(x, y, w, h);
0316:                } else {
0317:                    devClip = c.getIntersectionXYWH(x, y, w, h);
0318:                }
0319:                validateCompClip();
0320:            }
0321:
0322:            public void setDevClip(Rectangle r) {
0323:                setDevClip(r.x, r.y, r.width, r.height);
0324:            }
0325:
0326:            /**
0327:             * Constrain rendering for lightweight objects.
0328:             *
0329:             * REMIND: This method will back off to the "workaround"
0330:             * of using translate and clipRect if the Graphics
0331:             * to be constrained has a complex transform.  The
0332:             * drawback of the workaround is that the resulting
0333:             * clip and device origin cannot be "enforced".
0334:             *
0335:             * @exception IllegalStateException If the Graphics
0336:             * to be constrained has a complex transform.
0337:             */
0338:            public void constrain(int x, int y, int w, int h) {
0339:                if ((x | y) != 0) {
0340:                    translate(x, y);
0341:                }
0342:                if (transformState >= TRANSFORM_TRANSLATESCALE) {
0343:                    clipRect(0, 0, w, h);
0344:                    return;
0345:                }
0346:                x = constrainX = transX;
0347:                y = constrainY = transY;
0348:                w = Region.dimAdd(x, w);
0349:                h = Region.dimAdd(y, h);
0350:                Region c = constrainClip;
0351:                if (c == null) {
0352:                    c = Region.getInstanceXYXY(x, y, w, h);
0353:                } else {
0354:                    c = c.getIntersectionXYXY(x, y, w, h);
0355:                    if (c == constrainClip) {
0356:                        // Common case to ignore
0357:                        return;
0358:                    }
0359:                }
0360:                constrainClip = c;
0361:                if (!devClip.isInsideQuickCheck(c)) {
0362:                    devClip = devClip.getIntersection(c);
0363:                    validateCompClip();
0364:                }
0365:            }
0366:
0367:            protected static ValidatePipe invalidpipe = new ValidatePipe();
0368:
0369:            /*
0370:             * Invalidate the pipeline
0371:             */
0372:            protected void invalidatePipe() {
0373:                drawpipe = invalidpipe;
0374:                fillpipe = invalidpipe;
0375:                shapepipe = invalidpipe;
0376:                textpipe = invalidpipe;
0377:                imagepipe = invalidpipe;
0378:            }
0379:
0380:            public void validatePipe() {
0381:                surfaceData.validatePipe(this );
0382:            }
0383:
0384:            /*
0385:             * Intersect two Shapes by the simplest method, attempting to produce
0386:             * a simplified result.
0387:             * The boolean arguments keep1 and keep2 specify whether or not
0388:             * the first or second shapes can be modified during the operation
0389:             * or whether that shape must be "kept" unmodified.
0390:             */
0391:            Shape intersectShapes(Shape s1, Shape s2, boolean keep1,
0392:                    boolean keep2) {
0393:                if (s1 instanceof  Rectangle && s2 instanceof  Rectangle) {
0394:                    return ((Rectangle) s1).intersection((Rectangle) s2);
0395:                }
0396:                if (s1 instanceof  Rectangle2D) {
0397:                    return intersectRectShape((Rectangle2D) s1, s2, keep1,
0398:                            keep2);
0399:                } else if (s2 instanceof  Rectangle2D) {
0400:                    return intersectRectShape((Rectangle2D) s2, s1, keep2,
0401:                            keep1);
0402:                }
0403:                return intersectByArea(s1, s2, keep1, keep2);
0404:            }
0405:
0406:            /*
0407:             * Intersect a Rectangle with a Shape by the simplest method,
0408:             * attempting to produce a simplified result.
0409:             * The boolean arguments keep1 and keep2 specify whether or not
0410:             * the first or second shapes can be modified during the operation
0411:             * or whether that shape must be "kept" unmodified.
0412:             */
0413:            Shape intersectRectShape(Rectangle2D r, Shape s, boolean keep1,
0414:                    boolean keep2) {
0415:                if (s instanceof  Rectangle2D) {
0416:                    Rectangle2D r2 = (Rectangle2D) s;
0417:                    Rectangle2D outrect;
0418:                    if (!keep1) {
0419:                        outrect = r;
0420:                    } else if (!keep2) {
0421:                        outrect = r2;
0422:                    } else {
0423:                        outrect = new Rectangle2D.Float();
0424:                    }
0425:                    double x1 = Math.max(r.getX(), r2.getX());
0426:                    double x2 = Math.min(r.getX() + r.getWidth(), r2.getX()
0427:                            + r2.getWidth());
0428:                    double y1 = Math.max(r.getY(), r2.getY());
0429:                    double y2 = Math.min(r.getY() + r.getHeight(), r2.getY()
0430:                            + r2.getHeight());
0431:
0432:                    if (((x2 - x1) < 0) || ((y2 - y1) < 0))
0433:                        // Width or height is negative. No intersection.
0434:                        outrect.setFrameFromDiagonal(0, 0, 0, 0);
0435:                    else
0436:                        outrect.setFrameFromDiagonal(x1, y1, x2, y2);
0437:                    return outrect;
0438:                }
0439:                if (r.contains(s.getBounds2D())) {
0440:                    if (keep2) {
0441:                        s = cloneShape(s);
0442:                    }
0443:                    return s;
0444:                }
0445:                return intersectByArea(r, s, keep1, keep2);
0446:            }
0447:
0448:            protected static Shape cloneShape(Shape s) {
0449:                return new GeneralPath(s);
0450:            }
0451:
0452:            /*
0453:             * Intersect two Shapes using the Area class.  Presumably other
0454:             * attempts at simpler intersection methods proved fruitless.
0455:             * The boolean arguments keep1 and keep2 specify whether or not
0456:             * the first or second shapes can be modified during the operation
0457:             * or whether that shape must be "kept" unmodified.
0458:             * @see #intersectShapes
0459:             * @see #intersectRectShape
0460:             */
0461:            Shape intersectByArea(Shape s1, Shape s2, boolean keep1,
0462:                    boolean keep2) {
0463:                Area a1, a2;
0464:
0465:                // First see if we can find an overwriteable source shape
0466:                // to use as our destination area to avoid duplication.
0467:                if (!keep1 && (s1 instanceof  Area)) {
0468:                    a1 = (Area) s1;
0469:                } else if (!keep2 && (s2 instanceof  Area)) {
0470:                    a1 = (Area) s2;
0471:                    s2 = s1;
0472:                } else {
0473:                    a1 = new Area(s1);
0474:                }
0475:
0476:                if (s2 instanceof  Area) {
0477:                    a2 = (Area) s2;
0478:                } else {
0479:                    a2 = new Area(s2);
0480:                }
0481:
0482:                a1.intersect(a2);
0483:                if (a1.isRectangular()) {
0484:                    return a1.getBounds();
0485:                }
0486:
0487:                return a1;
0488:            }
0489:
0490:            /*
0491:             * Intersect usrClip bounds and device bounds to determine the composite
0492:             * rendering boundaries.
0493:             */
0494:            public Region getCompClip() {
0495:                if (!surfaceData.isValid()) {
0496:                    // revalidateAll() implicitly recalculcates the composite clip
0497:                    revalidateAll();
0498:                }
0499:
0500:                return clipRegion;
0501:            }
0502:
0503:            public Font getFont() {
0504:                if (font == null) {
0505:                    font = defaultFont;
0506:                }
0507:                return font;
0508:            }
0509:
0510:            private static final double[] IDENT_MATRIX = { 1, 0, 0, 1 };
0511:            private static final AffineTransform IDENT_ATX = new AffineTransform();
0512:
0513:            private static final int MINALLOCATED = 8;
0514:            private static final int TEXTARRSIZE = 17;
0515:            private static double[][] textTxArr = new double[TEXTARRSIZE][];
0516:            private static AffineTransform[] textAtArr = new AffineTransform[TEXTARRSIZE];
0517:
0518:            static {
0519:                for (int i = MINALLOCATED; i < TEXTARRSIZE; i++) {
0520:                    textTxArr[i] = new double[] { i, 0, 0, i };
0521:                    textAtArr[i] = new AffineTransform(textTxArr[i]);
0522:                }
0523:            }
0524:
0525:            // cached state for various draw[String,Char,Byte] optimizations
0526:            public FontInfo checkFontInfo(FontInfo info, Font font,
0527:                    FontRenderContext frc) {
0528:                /* Do not create a FontInfo object as part of construction of an
0529:                 * SG2D as its possible it may never be needed - ie if no text
0530:                 * is drawn using this SG2D.
0531:                 */
0532:                if (info == null) {
0533:                    info = new FontInfo();
0534:                }
0535:
0536:                float ptSize = font.getSize2D();
0537:                int txFontType;
0538:                AffineTransform devAt, textAt = null;
0539:                if (font.isTransformed()) {
0540:                    textAt = font.getTransform();
0541:                    textAt.scale(ptSize, ptSize);
0542:                    txFontType = textAt.getType();
0543:                    info.originX = (float) textAt.getTranslateX();
0544:                    info.originY = (float) textAt.getTranslateY();
0545:                    textAt.translate(-info.originX, -info.originY);
0546:                    if (transformState >= TRANSFORM_TRANSLATESCALE) {
0547:                        transform.getMatrix(info.devTx = new double[4]);
0548:                        devAt = new AffineTransform(info.devTx);
0549:                        textAt.preConcatenate(devAt);
0550:                    } else {
0551:                        info.devTx = IDENT_MATRIX;
0552:                        devAt = IDENT_ATX;
0553:                    }
0554:                    textAt.getMatrix(info.glyphTx = new double[4]);
0555:                    double shearx = textAt.getShearX();
0556:                    double scaley = textAt.getScaleY();
0557:                    if (shearx != 0) {
0558:                        scaley = Math.sqrt(shearx * shearx + scaley * scaley);
0559:                    }
0560:                    info.pixelHeight = (int) (Math.abs(scaley) + 0.5);
0561:                } else {
0562:                    txFontType = AffineTransform.TYPE_IDENTITY;
0563:                    info.originX = info.originY = 0;
0564:                    if (transformState >= TRANSFORM_TRANSLATESCALE) {
0565:                        transform.getMatrix(info.devTx = new double[4]);
0566:                        devAt = new AffineTransform(info.devTx);
0567:                        info.glyphTx = new double[4];
0568:                        for (int i = 0; i < 4; i++) {
0569:                            info.glyphTx[i] = info.devTx[i] * ptSize;
0570:                        }
0571:                        textAt = new AffineTransform(info.glyphTx);
0572:                        double shearx = transform.getShearX();
0573:                        double scaley = transform.getScaleY();
0574:                        if (shearx != 0) {
0575:                            scaley = Math.sqrt(shearx * shearx + scaley
0576:                                    * scaley);
0577:                        }
0578:                        info.pixelHeight = (int) (Math.abs(scaley * ptSize) + 0.5);
0579:                    } else {
0580:                        /* If the double represents a common integral, we
0581:                         * may have pre-allocated objects.
0582:                         * A "sparse" array be seems to be as fast as a switch
0583:                         * even for 3 or 4 pt sizes, and is more flexible.
0584:                         * This should perform comparably in single-threaded
0585:                         * rendering to the old code which synchronized on the
0586:                         * class and scale better on MP systems.
0587:                         */
0588:                        int pszInt = (int) ptSize;
0589:                        if (ptSize == pszInt && pszInt >= MINALLOCATED
0590:                                && pszInt < TEXTARRSIZE) {
0591:                            info.glyphTx = textTxArr[pszInt];
0592:                            textAt = textAtArr[pszInt];
0593:                            info.pixelHeight = pszInt;
0594:                        } else {
0595:                            info.pixelHeight = (int) (ptSize + 0.5);
0596:                        }
0597:                        if (textAt == null) {
0598:                            info.glyphTx = new double[] { ptSize, 0, 0, ptSize };
0599:                            textAt = new AffineTransform(info.glyphTx);
0600:                        }
0601:
0602:                        info.devTx = IDENT_MATRIX;
0603:                        devAt = IDENT_ATX;
0604:                    }
0605:                }
0606:
0607:                info.font2D = FontManager.getFont2D(font);
0608:
0609:                int fmhint = fractionalMetricsHint;
0610:                if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) {
0611:                    fmhint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
0612:                }
0613:                info.lcdSubPixPos = false; // conditionally set true in LCD mode.
0614:
0615:                /* The text anti-aliasing hints that are set by the client need
0616:                 * to be interpreted for the current state and stored in the
0617:                 * FontInfo.aahint which is what will actually be used and
0618:                 * will be one of OFF, ON, LCD_HRGB or LCD_VRGB.
0619:                 * This is what pipe selection code should typically refer to, not
0620:                 * textAntialiasHint. This means we are now evaluating the meaning
0621:                 * of "default" here. Any pipe that really cares about that will
0622:                 * also need to consult that variable.
0623:                 * Otherwise these are being used only as args to getStrike,
0624:                 * and are encapsulated in that object which is part of the
0625:                 * FontInfo, so we do not need to store them directly as fields
0626:                 * in the FontInfo object.
0627:                 * That could change if FontInfo's were more selectively
0628:                 * revalidated when graphics state changed. Presently this
0629:                 * method re-evaluates all fields in the fontInfo.
0630:                 * The strike doesn't need to know the RGB subpixel order. Just
0631:                 * if its H or V orientation, so if an LCD option is specified we
0632:                 * always pass in the RGB hint to the strike.
0633:                 * frc is non-null only if this is a GlyphVector. For reasons
0634:                 * which are probably a historical mistake the AA hint in a GV
0635:                 * is honoured when we render, overriding the Graphics setting.
0636:                 */
0637:                int aahint;
0638:                if (frc == null) {
0639:                    aahint = textAntialiasHint;
0640:                } else {
0641:                    aahint = ((SunHints.Value) frc.getAntiAliasingHint())
0642:                            .getIndex();
0643:                }
0644:                if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT) {
0645:                    if (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {
0646:                        aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
0647:                    } else {
0648:                        aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;
0649:                    }
0650:                } else {
0651:                    /* If we are in checkFontInfo because a rendering hint has been
0652:                     * set then all pipes are revalidated. But we can also
0653:                     * be here because setFont() has been called when the 'gasp'
0654:                     * hint is set, as then the font size determines the text pipe.
0655:                     * See comments in SunGraphics2d.setFont(Font).
0656:                     */
0657:                    if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP) {
0658:                        if (info.font2D.useAAForPtSize(info.pixelHeight)) {
0659:                            aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
0660:                        } else {
0661:                            aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;
0662:                        }
0663:                    } else if (aahint >= SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB) {
0664:                        /* loops for default rendering modes are installed in the SG2D
0665:                         * constructor. If there are none this will be null.
0666:                         * Not all compositing modes update the render loops, so
0667:                         * we also test that this is a mode we know should support
0668:                         * this. One minor issue is that the loops aren't necessarily
0669:                         * installed for a new rendering mode until after this
0670:                         * method is called during pipeline validation. So it is
0671:                         * theoretically possible that it was set to null for a
0672:                         * compositing mode, the composite is then set back to Src,
0673:                         * but the loop is still null when this is called and AA=ON
0674:                         * is installed instead of an LCD mode.
0675:                         * However this is done in the right order in SurfaceData.java
0676:                         * so this is not likely to be a problem - but not
0677:                         * guaranteed.
0678:                         */
0679:                        if (!surfaceData.canRenderLCDText(this )
0680:                        // 		      loops.drawGlyphListLCDLoop == null ||
0681:                        // 		      compositeState > COMP_ISCOPY ||
0682:                        // 		      paintState > PAINT_ALPHACOLOR
0683:                        ) {
0684:                            aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
0685:                        } else {
0686:                            info.lcdRGBOrder = true;
0687:                            /* Collapse these into just HRGB or VRGB.
0688:                             * Pipe selection code needs only to test for these two.
0689:                             * Since these both select the same pipe anyway its
0690:                             * tempting to collapse into one value. But they are
0691:                             * different strikes (glyph caches) so the distinction
0692:                             * needs to be made for that purpose.
0693:                             */
0694:                            if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HBGR) {
0695:                                aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
0696:                                info.lcdRGBOrder = false;
0697:                            } else if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VBGR) {
0698:                                aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB;
0699:                                info.lcdRGBOrder = false;
0700:                            }
0701:                            /* Support subpixel positioning only for the case in
0702:                             * which the horizontal resolution is increased
0703:                             */
0704:                            info.lcdSubPixPos = fmhint == SunHints.INTVAL_FRACTIONALMETRICS_ON
0705:                                    && aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
0706:                        }
0707:                    }
0708:                }
0709:                info.aaHint = aahint;
0710:                info.fontStrike = info.font2D.getStrike(font, devAt, textAt,
0711:                        aahint, fmhint);
0712:                return info;
0713:            }
0714:
0715:            public static boolean isRotated(double[] mtx) {
0716:                if ((mtx[0] == mtx[3]) && (mtx[1] == 0.0) && (mtx[2] == 0.0)
0717:                        && (mtx[0] > 0.0)) {
0718:                    return false;
0719:                }
0720:
0721:                return true;
0722:            }
0723:
0724:            public void setFont(Font font) {
0725:                /* replacing the reference equality test font != this.font with
0726:                 * !font.equals(this.font) did not yield any measurable difference
0727:                 * in testing, but there may be yet to be identified cases where it
0728:                 * is beneficial.
0729:                 */
0730:                if (font != null && font != this .font/*!font.equals(this.font)*/) {
0731:                    /* In the GASP AA case the textpipe depends on the glyph size
0732:                     * as determined by graphics and font transforms as well as the
0733:                     * font size, and information in the font. But we may invalidate
0734:                     * the pipe only to find that it made no difference. 
0735:                     * Deferring pipe invalidation to checkFontInfo won't work because
0736:                     * when called we may already be rendering to the wrong pipe.
0737:                     * So, if the font is transformed, or the graphics has more than
0738:                     * a simple scale, we'll take that as enough of a hint to
0739:                     * revalidate everything. But if they aren't we will 
0740:                     * use the font's point size to query the gasp table and see if
0741:                     * what it says matches what's currently being used, in which
0742:                     * case there's no need to invalidate the textpipe.
0743:                     * This should be sufficient for all typical uses cases.
0744:                     */
0745:                    if (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP
0746:                            && textpipe != invalidpipe
0747:                            && (transformState > TRANSFORM_ANY_TRANSLATE
0748:                                    || font.isTransformed() || fontInfo == null || // Precaution, if true shouldn't get here
0749:                            (fontInfo.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) != FontManager
0750:                                    .getFont2D(font).useAAForPtSize(
0751:                                            font.getSize()))) {
0752:                        textpipe = invalidpipe;
0753:                    }
0754:                    this .font = font;
0755:                    this .fontMetrics = null;
0756:                    this .validFontInfo = false;
0757:                }
0758:            }
0759:
0760:            public FontInfo getFontInfo() {
0761:                if (!validFontInfo) {
0762:                    this .fontInfo = checkFontInfo(this .fontInfo, font, null);
0763:                    validFontInfo = true;
0764:                }
0765:                return this .fontInfo;
0766:            }
0767:
0768:            /* Used by drawGlyphVector which specifies its own font. */
0769:            public FontInfo getGVFontInfo(Font font, FontRenderContext frc) {
0770:                if (glyphVectorFontInfo != null
0771:                        && glyphVectorFontInfo.font == font
0772:                        && glyphVectorFRC == frc) {
0773:                    return glyphVectorFontInfo;
0774:                } else {
0775:                    glyphVectorFRC = frc;
0776:                    return glyphVectorFontInfo = checkFontInfo(
0777:                            glyphVectorFontInfo, font, frc);
0778:                }
0779:            }
0780:
0781:            public FontMetrics getFontMetrics() {
0782:                if (this .fontMetrics != null) {
0783:                    return this .fontMetrics;
0784:                }
0785:                /* NB the constructor and the setter disallow "font" being null */
0786:                return this .fontMetrics = FontDesignMetrics.getMetrics(font,
0787:                        getFontRenderContext());
0788:            }
0789:
0790:            public FontMetrics getFontMetrics(Font font) {
0791:                if ((this .fontMetrics != null) && (font == this .font)) {
0792:                    return this .fontMetrics;
0793:                }
0794:                FontMetrics fm = FontDesignMetrics.getMetrics(font,
0795:                        getFontRenderContext());
0796:
0797:                if (this .font == font) {
0798:                    this .fontMetrics = fm;
0799:                }
0800:                return fm;
0801:            }
0802:
0803:            /**
0804:             * Checks to see if a Path intersects the specified Rectangle in device
0805:             * space.  The rendering attributes taken into account include the
0806:             * clip, transform, and stroke attributes.
0807:             * @param rect The area in device space to check for a hit.
0808:             * @param p The path to check for a hit.
0809:             * @param onStroke Flag to choose between testing the stroked or
0810:             * the filled path.
0811:             * @return True if there is a hit, false otherwise.
0812:             * @see #setStroke
0813:             * @see #fillPath
0814:             * @see #drawPath
0815:             * @see #transform
0816:             * @see #setTransform
0817:             * @see #clip
0818:             * @see #setClip
0819:             */
0820:            public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
0821:                if (onStroke) {
0822:                    s = stroke.createStrokedShape(s);
0823:                }
0824:
0825:                s = transformShape(s);
0826:                if ((constrainX | constrainY) != 0) {
0827:                    rect = new Rectangle(rect);
0828:                    rect.translate(constrainX, constrainY);
0829:                }
0830:
0831:                return s.intersects(rect);
0832:            }
0833:
0834:            /**
0835:             * Return the ColorModel associated with this Graphics2D.
0836:             */
0837:            public ColorModel getDeviceColorModel() {
0838:                return surfaceData.getColorModel();
0839:            }
0840:
0841:            /**
0842:             * Return the device configuration associated with this Graphics2D.
0843:             */
0844:            public GraphicsConfiguration getDeviceConfiguration() {
0845:                return surfaceData.getDeviceConfiguration();
0846:            }
0847:
0848:            /**
0849:             * Return the SurfaceData object assigned to manage the destination
0850:             * drawable surface of this Graphics2D.
0851:             */
0852:            public final SurfaceData getSurfaceData() {
0853:                return surfaceData;
0854:            }
0855:
0856:            /**
0857:             * Sets the Composite in the current graphics state. Composite is used
0858:             * in all drawing methods such as drawImage, drawString, drawPath,
0859:             * and fillPath.  It specifies how new pixels are to be combined with
0860:             * the existing pixels on the graphics device in the rendering process.
0861:             * @param comp The Composite object to be used for drawing.
0862:             * @see java.awt.Graphics#setXORMode
0863:             * @see java.awt.Graphics#setPaintMode
0864:             * @see AlphaComposite
0865:             */
0866:            public void setComposite(Composite comp) {
0867:                if (composite == comp) {
0868:                    return;
0869:                }
0870:                int newCompState;
0871:                CompositeType newCompType;
0872:                if (comp instanceof  AlphaComposite) {
0873:                    AlphaComposite alphacomp = (AlphaComposite) comp;
0874:                    newCompType = CompositeType.forAlphaComposite(alphacomp);
0875:                    if (newCompType == CompositeType.SrcOverNoEa) {
0876:                        if (paintState == PAINT_OPAQUECOLOR
0877:                                || (paintState > PAINT_ALPHACOLOR && paint
0878:                                        .getTransparency() == Transparency.OPAQUE)) {
0879:                            newCompState = COMP_ISCOPY;
0880:                        } else {
0881:                            newCompState = COMP_ALPHA;
0882:                        }
0883:                    } else if (newCompType == CompositeType.SrcNoEa
0884:                            || newCompType == CompositeType.Src
0885:                            || newCompType == CompositeType.Clear) {
0886:                        newCompState = COMP_ISCOPY;
0887:                    } else if (surfaceData.getTransparency() == Transparency.OPAQUE
0888:                            && newCompType == CompositeType.SrcIn) {
0889:                        newCompState = COMP_ISCOPY;
0890:                    } else {
0891:                        newCompState = COMP_ALPHA;
0892:                    }
0893:                } else if (comp instanceof  XORComposite) {
0894:                    newCompState = COMP_XOR;
0895:                    newCompType = CompositeType.Xor;
0896:                } else if (comp == null) {
0897:                    throw new IllegalArgumentException("null Composite");
0898:                } else {
0899:                    surfaceData.checkCustomComposite();
0900:                    newCompState = COMP_CUSTOM;
0901:                    newCompType = CompositeType.General;
0902:                }
0903:                if (compositeState != newCompState || imageComp != newCompType) {
0904:                    compositeState = newCompState;
0905:                    imageComp = newCompType;
0906:                    invalidatePipe();
0907:                    validFontInfo = false;
0908:                }
0909:                composite = comp;
0910:                if (paintState <= PAINT_ALPHACOLOR) {
0911:                    validateColor();
0912:                }
0913:            }
0914:
0915:            /**
0916:             * Sets the Paint in the current graphics state.
0917:             * @param paint The Paint object to be used to generate color in
0918:             * the rendering process.
0919:             * @see java.awt.Graphics#setColor
0920:             * @see GradientPaint
0921:             * @see TexturePaint
0922:             */
0923:            public void setPaint(Paint paint) {
0924:                if (paint instanceof  Color) {
0925:                    setColor((Color) paint);
0926:                    return;
0927:                }
0928:                if (paint == null || this .paint == paint) {
0929:                    return;
0930:                }
0931:                this .paint = paint;
0932:                if (imageComp == CompositeType.SrcOverNoEa) {
0933:                    // special case where compState depends on opacity of paint
0934:                    if (paint.getTransparency() == Transparency.OPAQUE) {
0935:                        if (compositeState != COMP_ISCOPY) {
0936:                            compositeState = COMP_ISCOPY;
0937:                        }
0938:                    } else {
0939:                        if (compositeState == COMP_ISCOPY) {
0940:                            compositeState = COMP_ALPHA;
0941:                        }
0942:                    }
0943:                }
0944:                Class paintClass = paint.getClass();
0945:                if (paintClass == GradientPaint.class) {
0946:                    paintState = PAINT_GRADIENT;
0947:                } else if (paintClass == LinearGradientPaint.class) {
0948:                    paintState = PAINT_LIN_GRADIENT;
0949:                } else if (paintClass == RadialGradientPaint.class) {
0950:                    paintState = PAINT_RAD_GRADIENT;
0951:                } else if (paintClass == TexturePaint.class) {
0952:                    paintState = PAINT_TEXTURE;
0953:                } else {
0954:                    paintState = PAINT_CUSTOM;
0955:                }
0956:                validFontInfo = false;
0957:                invalidatePipe();
0958:            }
0959:
0960:            static final int NON_UNIFORM_SCALE_MASK = (AffineTransform.TYPE_GENERAL_TRANSFORM | AffineTransform.TYPE_GENERAL_SCALE);
0961:            public static final double MinPenSizeAA = sun.java2d.pipe.RenderingEngine
0962:                    .getInstance().getMinimumAAPenSize();
0963:            public static final double MinPenSizeAASquared = (MinPenSizeAA * MinPenSizeAA);
0964:            // Since inaccuracies in the trig package can cause us to
0965:            // calculated a rotated pen width of just slightly greater
0966:            // than 1.0, we add a fudge factor to our comparison value
0967:            // here so that we do not misclassify single width lines as
0968:            // wide lines under certain rotations.
0969:            public static final double MinPenSizeSquared = 1.000000001;
0970:
0971:            private void validateBasicStroke(BasicStroke bs) {
0972:                boolean aa = (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON);
0973:                if (transformState < TRANSFORM_TRANSLATESCALE) {
0974:                    if (aa) {
0975:                        if (bs.getLineWidth() <= MinPenSizeAA) {
0976:                            if (bs.getDashArray() == null) {
0977:                                strokeState = STROKE_THIN;
0978:                            } else {
0979:                                strokeState = STROKE_THINDASHED;
0980:                            }
0981:                        } else {
0982:                            strokeState = STROKE_WIDE;
0983:                        }
0984:                    } else {
0985:                        if (bs == defaultStroke) {
0986:                            strokeState = STROKE_THIN;
0987:                        } else if (bs.getLineWidth() <= 1.0f) {
0988:                            if (bs.getDashArray() == null) {
0989:                                strokeState = STROKE_THIN;
0990:                            } else {
0991:                                strokeState = STROKE_THINDASHED;
0992:                            }
0993:                        } else {
0994:                            strokeState = STROKE_WIDE;
0995:                        }
0996:                    }
0997:                } else {
0998:                    double widthsquared;
0999:                    if ((transform.getType() & NON_UNIFORM_SCALE_MASK) == 0) {
1000:                        /* sqrt omitted, compare to squared limits below. */
1001:                        widthsquared = Math.abs(transform.getDeterminant());
1002:                    } else {
1003:                        /* First calculate the "maximum scale" of this transform. */
1004:                        double A = transform.getScaleX(); // m00
1005:                        double C = transform.getShearX(); // m01
1006:                        double B = transform.getShearY(); // m10
1007:                        double D = transform.getScaleY(); // m11
1008:
1009:                        /*
1010:                         * Given a 2 x 2 affine matrix [ A B ] such that
1011:                         *                             [ C D ]
1012:                         * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
1013:                         * find the maximum magnitude (norm) of the vector v'
1014:                         * with the constraint (x^2 + y^2 = 1).
1015:                         * The equation to maximize is
1016:                         *     |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
1017:                         * or  |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
1018:                         * Since sqrt is monotonic we can maximize |v'|^2
1019:                         * instead and plug in the substitution y = sqrt(1 - x^2).
1020:                         * Trigonometric equalities can then be used to get
1021:                         * rid of most of the sqrt terms.
1022:                         */
1023:                        double EA = A * A + B * B; // x^2 coefficient
1024:                        double EB = 2 * (A * C + B * D); // xy coefficient
1025:                        double EC = C * C + D * D; // y^2 coefficient
1026:
1027:                        /*
1028:                         * There is a lot of calculus omitted here.
1029:                         *
1030:                         * Conceptually, in the interests of understanding the
1031:                         * terms that the calculus produced we can consider
1032:                         * that EA and EC end up providing the lengths along
1033:                         * the major axes and the hypot term ends up being an
1034:                         * adjustment for the additional length along the off-axis
1035:                         * angle of rotated or sheared ellipses as well as an
1036:                         * adjustment for the fact that the equation below
1037:                         * averages the two major axis lengths.  (Notice that
1038:                         * the hypot term contains a part which resolves to the
1039:                         * difference of these two axis lengths in the absence
1040:                         * of rotation.)
1041:                         *
1042:                         * In the calculus, the ratio of the EB and (EA-EC) terms
1043:                         * ends up being the tangent of 2*theta where theta is
1044:                         * the angle that the long axis of the ellipse makes
1045:                         * with the horizontal axis.  Thus, this equation is
1046:                         * calculating the length of the hypotenuse of a triangle
1047:                         * along that axis.
1048:                         */
1049:                        double hypot = Math.sqrt(EB * EB + (EA - EC)
1050:                                * (EA - EC));
1051:
1052:                        /* sqrt omitted, compare to squared limits below. */
1053:                        widthsquared = ((EA + EC + hypot) / 2.0);
1054:                    }
1055:                    if (bs != defaultStroke) {
1056:                        widthsquared *= bs.getLineWidth() * bs.getLineWidth();
1057:                    }
1058:                    if (widthsquared <= (aa ? MinPenSizeAASquared
1059:                            : MinPenSizeSquared)) {
1060:                        if (bs.getDashArray() == null) {
1061:                            strokeState = STROKE_THIN;
1062:                        } else {
1063:                            strokeState = STROKE_THINDASHED;
1064:                        }
1065:                    } else {
1066:                        strokeState = STROKE_WIDE;
1067:                    }
1068:                }
1069:            }
1070:
1071:            /*
1072:             * Sets the Stroke in the current graphics state.
1073:             * @param s The Stroke object to be used to stroke a Path in
1074:             * the rendering process.
1075:             * @see BasicStroke
1076:             */
1077:            public void setStroke(Stroke s) {
1078:                if (s == null) {
1079:                    throw new IllegalArgumentException("null Stroke");
1080:                }
1081:                int saveStrokeState = strokeState;
1082:                stroke = s;
1083:                if (s instanceof  BasicStroke) {
1084:                    validateBasicStroke((BasicStroke) s);
1085:                } else {
1086:                    strokeState = STROKE_CUSTOM;
1087:                }
1088:                if (strokeState != saveStrokeState) {
1089:                    invalidatePipe();
1090:                }
1091:            }
1092:
1093:            /**
1094:             * Sets the preferences for the rendering algorithms.
1095:             * Hint categories include controls for rendering quality and
1096:             * overall time/quality trade-off in the rendering process.
1097:             * @param hintKey The key of hint to be set. The strings are
1098:             * defined in the RenderingHints class.  
1099:             * @param hintValue The value indicating preferences for the specified
1100:             * hint category. These strings are defined in the RenderingHints
1101:             * class.  
1102:             * @see RenderingHints
1103:             */
1104:            public void setRenderingHint(Key hintKey, Object hintValue) {
1105:                // If we recognize the key, we must recognize the value
1106:                //     otherwise throw an IllegalArgumentException
1107:                //     and do not change the Hints object
1108:                // If we do not recognize the key, just pass it through
1109:                //     to the Hints object untouched
1110:                if (!hintKey.isCompatibleValue(hintValue)) {
1111:                    throw new IllegalArgumentException(hintValue
1112:                            + " is not compatible with " + hintKey);
1113:                }
1114:                if (hintKey instanceof  SunHints.Key) {
1115:                    boolean stateChanged;
1116:                    boolean textStateChanged = false;
1117:                    boolean recognized = true;
1118:                    SunHints.Key sunKey = (SunHints.Key) hintKey;
1119:                    int newHint;
1120:                    if (sunKey == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST) {
1121:                        newHint = ((Integer) hintValue).intValue();
1122:                    } else {
1123:                        newHint = ((SunHints.Value) hintValue).getIndex();
1124:                    }
1125:                    switch (sunKey.getIndex()) {
1126:                    case SunHints.INTKEY_RENDERING:
1127:                        stateChanged = (renderHint != newHint);
1128:                        if (stateChanged) {
1129:                            renderHint = newHint;
1130:                            if (interpolationHint == -1) {
1131:                                interpolationType = (newHint == SunHints.INTVAL_RENDER_QUALITY ? AffineTransformOp.TYPE_BILINEAR
1132:                                        : AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
1133:                            }
1134:                        }
1135:                        break;
1136:                    case SunHints.INTKEY_ANTIALIASING:
1137:                        stateChanged = (antialiasHint != newHint);
1138:                        antialiasHint = newHint;
1139:                        if (stateChanged) {
1140:                            textStateChanged = (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT);
1141:                            if (strokeState != STROKE_CUSTOM) {
1142:                                validateBasicStroke((BasicStroke) stroke);
1143:                            }
1144:                        }
1145:                        break;
1146:                    case SunHints.INTKEY_TEXT_ANTIALIASING:
1147:                        stateChanged = (textAntialiasHint != newHint);
1148:                        textStateChanged = stateChanged;
1149:                        textAntialiasHint = newHint;
1150:                        break;
1151:                    case SunHints.INTKEY_FRACTIONALMETRICS:
1152:                        stateChanged = (fractionalMetricsHint != newHint);
1153:                        textStateChanged = stateChanged;
1154:                        fractionalMetricsHint = newHint;
1155:                        break;
1156:                    case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
1157:                        stateChanged = false;
1158:                        /* Already have validated it is an int 100 <= newHint <= 250 */
1159:                        lcdTextContrast = newHint;
1160:                        break;
1161:                    case SunHints.INTKEY_INTERPOLATION:
1162:                        interpolationHint = newHint;
1163:                        switch (newHint) {
1164:                        case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1165:                            newHint = AffineTransformOp.TYPE_BICUBIC;
1166:                            break;
1167:                        case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1168:                            newHint = AffineTransformOp.TYPE_BILINEAR;
1169:                            break;
1170:                        default:
1171:                        case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1172:                            newHint = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
1173:                            break;
1174:                        }
1175:                        stateChanged = (interpolationType != newHint);
1176:                        interpolationType = newHint;
1177:                        break;
1178:                    case SunHints.INTKEY_STROKE_CONTROL:
1179:                        stateChanged = (strokeHint != newHint);
1180:                        strokeHint = newHint;
1181:                        break;
1182:                    default:
1183:                        recognized = false;
1184:                        stateChanged = false;
1185:                        break;
1186:                    }
1187:                    if (recognized) {
1188:                        if (stateChanged) {
1189:                            invalidatePipe();
1190:                            if (textStateChanged) {
1191:                                fontMetrics = null;
1192:                                this .cachedFRC = null;
1193:                                validFontInfo = false;
1194:                                this .glyphVectorFontInfo = null;
1195:                            }
1196:                        }
1197:                        if (hints != null) {
1198:                            hints.put(hintKey, hintValue);
1199:                        }
1200:                        return;
1201:                    }
1202:                }
1203:                // Nothing we recognize so none of "our state" has changed
1204:                if (hints == null) {
1205:                    hints = makeHints(null);
1206:                }
1207:                hints.put(hintKey, hintValue);
1208:            }
1209:
1210:            /**
1211:             * Returns the preferences for the rendering algorithms.
1212:             * @param hintCategory The category of hint to be set. The strings
1213:             * are defined in the RenderingHints class.
1214:             * @return The preferences for rendering algorithms. The strings
1215:             * are defined in the RenderingHints class.
1216:             * @see RenderingHints
1217:             */
1218:            public Object getRenderingHint(Key hintKey) {
1219:                if (hints != null) {
1220:                    return hints.get(hintKey);
1221:                }
1222:                if (!(hintKey instanceof  SunHints.Key)) {
1223:                    return null;
1224:                }
1225:                int keyindex = ((SunHints.Key) hintKey).getIndex();
1226:                switch (keyindex) {
1227:                case SunHints.INTKEY_RENDERING:
1228:                    return SunHints.Value.get(SunHints.INTKEY_RENDERING,
1229:                            renderHint);
1230:                case SunHints.INTKEY_ANTIALIASING:
1231:                    return SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,
1232:                            antialiasHint);
1233:                case SunHints.INTKEY_TEXT_ANTIALIASING:
1234:                    return SunHints.Value.get(
1235:                            SunHints.INTKEY_TEXT_ANTIALIASING,
1236:                            textAntialiasHint);
1237:                case SunHints.INTKEY_FRACTIONALMETRICS:
1238:                    return SunHints.Value.get(
1239:                            SunHints.INTKEY_FRACTIONALMETRICS,
1240:                            fractionalMetricsHint);
1241:                case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
1242:                    return new Integer(lcdTextContrast);
1243:                case SunHints.INTKEY_INTERPOLATION:
1244:                    switch (interpolationHint) {
1245:                    case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1246:                        return SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1247:                    case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1248:                        return SunHints.VALUE_INTERPOLATION_BILINEAR;
1249:                    case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1250:                        return SunHints.VALUE_INTERPOLATION_BICUBIC;
1251:                    }
1252:                    return null;
1253:                case SunHints.INTKEY_STROKE_CONTROL:
1254:                    return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
1255:                            strokeHint);
1256:                }
1257:                return null;
1258:            }
1259:
1260:            /**
1261:             * Sets the preferences for the rendering algorithms.
1262:             * Hint categories include controls for rendering quality and
1263:             * overall time/quality trade-off in the rendering process.
1264:             * @param hints The rendering hints to be set
1265:             * @see RenderingHints
1266:             */
1267:            public void setRenderingHints(Map<?, ?> hints) {
1268:                this .hints = null;
1269:                renderHint = SunHints.INTVAL_RENDER_DEFAULT;
1270:                antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
1271:                textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
1272:                fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
1273:                lcdTextContrast = lcdTextContrastDefaultValue;
1274:                interpolationHint = -1;
1275:                interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
1276:                boolean customHintPresent = false;
1277:                Iterator iter = hints.keySet().iterator();
1278:                while (iter.hasNext()) {
1279:                    Object key = iter.next();
1280:                    if (key == SunHints.KEY_RENDERING
1281:                            || key == SunHints.KEY_ANTIALIASING
1282:                            || key == SunHints.KEY_TEXT_ANTIALIASING
1283:                            || key == SunHints.KEY_FRACTIONALMETRICS
1284:                            || key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST
1285:                            || key == SunHints.KEY_STROKE_CONTROL
1286:                            || key == SunHints.KEY_INTERPOLATION) {
1287:                        setRenderingHint((Key) key, hints.get(key));
1288:                    } else {
1289:                        customHintPresent = true;
1290:                    }
1291:                }
1292:                if (customHintPresent) {
1293:                    this .hints = makeHints(hints);
1294:                }
1295:                invalidatePipe();
1296:            }
1297:
1298:            /**
1299:             * Adds a number of preferences for the rendering algorithms.
1300:             * Hint categories include controls for rendering quality and
1301:             * overall time/quality trade-off in the rendering process.
1302:             * @param hints The rendering hints to be set
1303:             * @see RenderingHints
1304:             */
1305:            public void addRenderingHints(Map<?, ?> hints) {
1306:                boolean customHintPresent = false;
1307:                Iterator iter = hints.keySet().iterator();
1308:                while (iter.hasNext()) {
1309:                    Object key = iter.next();
1310:                    if (key == SunHints.KEY_RENDERING
1311:                            || key == SunHints.KEY_ANTIALIASING
1312:                            || key == SunHints.KEY_TEXT_ANTIALIASING
1313:                            || key == SunHints.KEY_FRACTIONALMETRICS
1314:                            || key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST
1315:                            || key == SunHints.KEY_STROKE_CONTROL
1316:                            || key == SunHints.KEY_INTERPOLATION) {
1317:                        setRenderingHint((Key) key, hints.get(key));
1318:                    } else {
1319:                        customHintPresent = true;
1320:                    }
1321:                }
1322:                if (customHintPresent) {
1323:                    if (this .hints == null) {
1324:                        this .hints = makeHints(hints);
1325:                    } else {
1326:                        this .hints.putAll(hints);
1327:                    }
1328:                }
1329:            }
1330:
1331:            /**
1332:             * Gets the preferences for the rendering algorithms.
1333:             * Hint categories include controls for rendering quality and
1334:             * overall time/quality trade-off in the rendering process.
1335:             * @see RenderingHints
1336:             */
1337:            public RenderingHints getRenderingHints() {
1338:                if (hints == null) {
1339:                    return makeHints(null);
1340:                } else {
1341:                    return (RenderingHints) hints.clone();
1342:                }
1343:            }
1344:
1345:            RenderingHints makeHints(Map hints) {
1346:                RenderingHints model = new RenderingHints(hints);
1347:                model.put(SunHints.KEY_RENDERING, SunHints.Value.get(
1348:                        SunHints.INTKEY_RENDERING, renderHint));
1349:                model.put(SunHints.KEY_ANTIALIASING, SunHints.Value.get(
1350:                        SunHints.INTKEY_ANTIALIASING, antialiasHint));
1351:                model.put(SunHints.KEY_TEXT_ANTIALIASING, SunHints.Value.get(
1352:                        SunHints.INTKEY_TEXT_ANTIALIASING, textAntialiasHint));
1353:                model.put(SunHints.KEY_FRACTIONALMETRICS, SunHints.Value.get(
1354:                        SunHints.INTKEY_FRACTIONALMETRICS,
1355:                        fractionalMetricsHint));
1356:                model.put(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST,
1357:                        new Integer(lcdTextContrast));
1358:                Object value;
1359:                switch (interpolationHint) {
1360:                case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1361:                    value = SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1362:                    break;
1363:                case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1364:                    value = SunHints.VALUE_INTERPOLATION_BILINEAR;
1365:                    break;
1366:                case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1367:                    value = SunHints.VALUE_INTERPOLATION_BICUBIC;
1368:                    break;
1369:                default:
1370:                    value = null;
1371:                    break;
1372:                }
1373:                if (value != null) {
1374:                    model.put(SunHints.KEY_INTERPOLATION, value);
1375:                }
1376:                model.put(SunHints.KEY_STROKE_CONTROL, SunHints.Value.get(
1377:                        SunHints.INTKEY_STROKE_CONTROL, strokeHint));
1378:                return model;
1379:            }
1380:
1381:            /**
1382:             * Concatenates the current transform of this Graphics2D with a
1383:             * translation transformation.
1384:             * This is equivalent to calling transform(T), where T is an
1385:             * AffineTransform represented by the following matrix:
1386:             * <pre>
1387:             *		[   1    0    tx  ]
1388:             *		[   0    1    ty  ]
1389:             *		[   0    0    1   ]
1390:             * </pre>
1391:             */
1392:            public void translate(double tx, double ty) {
1393:                transform.translate(tx, ty);
1394:                invalidateTransform();
1395:            }
1396:
1397:            /**
1398:             * Concatenates the current transform of this Graphics2D with a
1399:             * rotation transformation.
1400:             * This is equivalent to calling transform(R), where R is an
1401:             * AffineTransform represented by the following matrix:
1402:             * <pre>
1403:             *		[   cos(theta)    -sin(theta)    0   ]
1404:             *		[   sin(theta)     cos(theta)    0   ]
1405:             *		[       0              0         1   ]
1406:             * </pre>
1407:             * Rotating with a positive angle theta rotates points on the positive
1408:             * x axis toward the positive y axis.
1409:             * @param theta The angle of rotation in radians.
1410:             */
1411:            public void rotate(double theta) {
1412:                transform.rotate(theta);
1413:                invalidateTransform();
1414:            }
1415:
1416:            /**
1417:             * Concatenates the current transform of this Graphics2D with a
1418:             * translated rotation transformation.
1419:             * This is equivalent to the following sequence of calls:
1420:             * <pre>
1421:             *		translate(x, y);
1422:             *		rotate(theta);
1423:             *		translate(-x, -y);
1424:             * </pre>
1425:             * Rotating with a positive angle theta rotates points on the positive
1426:             * x axis toward the positive y axis.
1427:             * @param theta The angle of rotation in radians.
1428:             * @param x The x coordinate of the origin of the rotation
1429:             * @param y The x coordinate of the origin of the rotation
1430:             */
1431:            public void rotate(double theta, double x, double y) {
1432:                transform.rotate(theta, x, y);
1433:                invalidateTransform();
1434:            }
1435:
1436:            /**
1437:             * Concatenates the current transform of this Graphics2D with a
1438:             * scaling transformation.
1439:             * This is equivalent to calling transform(S), where S is an
1440:             * AffineTransform represented by the following matrix:
1441:             * <pre>
1442:             *		[   sx   0    0   ]
1443:             *		[   0    sy   0   ]
1444:             *		[   0    0    1   ]
1445:             * </pre>
1446:             */
1447:            public void scale(double sx, double sy) {
1448:                transform.scale(sx, sy);
1449:                invalidateTransform();
1450:            }
1451:
1452:            /**
1453:             * Concatenates the current transform of this Graphics2D with a
1454:             * shearing transformation.
1455:             * This is equivalent to calling transform(SH), where SH is an
1456:             * AffineTransform represented by the following matrix:
1457:             * <pre>
1458:             *		[   1   shx   0   ]
1459:             *		[  shy   1    0   ]
1460:             *		[   0    0    1   ]
1461:             * </pre>
1462:             * @param shx The factor by which coordinates are shifted towards the
1463:             * positive X axis direction according to their Y coordinate
1464:             * @param shy The factor by which coordinates are shifted towards the
1465:             * positive Y axis direction according to their X coordinate
1466:             */
1467:            public void shear(double shx, double shy) {
1468:                transform.shear(shx, shy);
1469:                invalidateTransform();
1470:            }
1471:
1472:            /**
1473:             * Composes a Transform object with the transform in this
1474:             * Graphics2D according to the rule last-specified-first-applied.
1475:             * If the currrent transform is Cx, the result of composition
1476:             * with Tx is a new transform Cx'.  Cx' becomes the current
1477:             * transform for this Graphics2D.
1478:             * Transforming a point p by the updated transform Cx' is
1479:             * equivalent to first transforming p by Tx and then transforming
1480:             * the result by the original transform Cx.  In other words,
1481:             * Cx'(p) = Cx(Tx(p)).
1482:             * A copy of the Tx is made, if necessary, so further
1483:             * modifications to Tx do not affect rendering.
1484:             * @param Tx The Transform object to be composed with the current
1485:             * transform.
1486:             * @see #setTransform
1487:             * @see AffineTransform
1488:             */
1489:            public void transform(AffineTransform xform) {
1490:                this .transform.concatenate(xform);
1491:                invalidateTransform();
1492:            }
1493:
1494:            /**
1495:             * Translate
1496:             */
1497:            public void translate(int x, int y) {
1498:                transform.translate(x, y);
1499:                if (transformState <= TRANSFORM_INT_TRANSLATE) {
1500:                    transX += x;
1501:                    transY += y;
1502:                    transformState = (((transX | transY) == 0) ? TRANSFORM_ISIDENT
1503:                            : TRANSFORM_INT_TRANSLATE);
1504:                } else {
1505:                    invalidateTransform();
1506:                }
1507:            }
1508:
1509:            /**
1510:             * Sets the Transform in the current graphics state.
1511:             * @param Tx The Transform object to be used in the rendering process.
1512:             * @see #transform
1513:             * @see TransformChain
1514:             * @see AffineTransform
1515:             */
1516:            public void setTransform(AffineTransform Tx) {
1517:                if ((constrainX | constrainY) == 0) {
1518:                    transform.setTransform(Tx);
1519:                } else {
1520:                    transform.setToTranslation(constrainX, constrainY);
1521:                    transform.concatenate(Tx);
1522:                }
1523:                invalidateTransform();
1524:            }
1525:
1526:            protected void invalidateTransform() {
1527:                int type = transform.getType();
1528:                int origTransformState = transformState;
1529:                if (type == AffineTransform.TYPE_IDENTITY) {
1530:                    transformState = TRANSFORM_ISIDENT;
1531:                    transX = transY = 0;
1532:                } else if (type == AffineTransform.TYPE_TRANSLATION) {
1533:                    double dtx = transform.getTranslateX();
1534:                    double dty = transform.getTranslateY();
1535:                    transX = (int) Math.floor(dtx + 0.5);
1536:                    transY = (int) Math.floor(dty + 0.5);
1537:                    if (dtx == transX && dty == transY) {
1538:                        transformState = TRANSFORM_INT_TRANSLATE;
1539:                    } else {
1540:                        transformState = TRANSFORM_ANY_TRANSLATE;
1541:                    }
1542:                } else if ((type & (AffineTransform.TYPE_FLIP
1543:                        | AffineTransform.TYPE_MASK_ROTATION | AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0) {
1544:                    transformState = TRANSFORM_TRANSLATESCALE;
1545:                    transX = transY = 0;
1546:                } else {
1547:                    transformState = TRANSFORM_GENERIC;
1548:                    transX = transY = 0;
1549:                }
1550:
1551:                if (transformState >= TRANSFORM_TRANSLATESCALE
1552:                        || origTransformState >= TRANSFORM_TRANSLATESCALE) {
1553:                    /* Its only in this case that the previous or current transform
1554:                     * was more than a translate that font info is invalidated
1555:                     */
1556:                    cachedFRC = null;
1557:                    this .validFontInfo = false;
1558:                    this .fontMetrics = null;
1559:                    this .glyphVectorFontInfo = null;
1560:
1561:                    if (transformState != origTransformState) {
1562:                        invalidatePipe();
1563:                    }
1564:                }
1565:                if (strokeState != STROKE_CUSTOM) {
1566:                    validateBasicStroke((BasicStroke) stroke);
1567:                }
1568:            }
1569:
1570:            /**
1571:             * Returns the current Transform in the Graphics2D state.
1572:             * @see #transform
1573:             * @see #setTransform
1574:             */
1575:            public AffineTransform getTransform() {
1576:                if ((constrainX | constrainY) == 0) {
1577:                    return new AffineTransform(transform);
1578:                }
1579:                AffineTransform tx = AffineTransform.getTranslateInstance(
1580:                        -constrainX, -constrainY);
1581:                tx.concatenate(transform);
1582:                return tx;
1583:            }
1584:
1585:            /**
1586:             * Returns the current Transform ignoring the "constrain"
1587:             * rectangle.
1588:             */
1589:            public AffineTransform cloneTransform() {
1590:                return new AffineTransform(transform);
1591:            }
1592:
1593:            /**
1594:             * Returns the current Paint in the Graphics2D state.
1595:             * @see #setPaint
1596:             * @see java.awt.Graphics#setColor
1597:             */
1598:            public Paint getPaint() {
1599:                return paint;
1600:            }
1601:
1602:            /**
1603:             * Returns the current Composite in the Graphics2D state.
1604:             * @see #setComposite
1605:             */
1606:            public Composite getComposite() {
1607:                return composite;
1608:            }
1609:
1610:            public Color getColor() {
1611:                return foregroundColor;
1612:            }
1613:
1614:            /*
1615:             * Validate the eargb and pixel fields against the current color.
1616:             *
1617:             * The eargb field must take into account the extraAlpha
1618:             * value of an AlphaComposite.  It may also take into account
1619:             * the Fsrc Porter-Duff blending function if such a function is
1620:             * a constant (see handling of Clear mode below).  For instance,
1621:             * by factoring in the (Fsrc == 0) state of the Clear mode we can
1622:             * use a SrcNoEa loop just as easily as a general Alpha loop
1623:             * since the math will be the same in both cases.
1624:             *
1625:             * The pixel field will always be the best pixel data choice for
1626:             * the final result of all calculations applied to the eargb field.
1627:             *
1628:             * Note that this method is only necessary under the following
1629:             * conditions:
1630:             *     (paintState <= PAINT_ALPHA_COLOR &&
1631:             *      compositeState <= COMP_CUSTOM)
1632:             * though nothing bad will happen if it is run in other states.
1633:             */
1634:            final void validateColor() {
1635:                int eargb;
1636:                if (imageComp == CompositeType.Clear) {
1637:                    eargb = 0;
1638:                } else {
1639:                    eargb = foregroundColor.getRGB();
1640:                    if (compositeState <= COMP_ALPHA
1641:                            && imageComp != CompositeType.SrcNoEa
1642:                            && imageComp != CompositeType.SrcOverNoEa) {
1643:                        AlphaComposite alphacomp = (AlphaComposite) composite;
1644:                        int a = Math.round(alphacomp.getAlpha()
1645:                                * (eargb >>> 24));
1646:                        eargb = (eargb & 0x00ffffff) | (a << 24);
1647:                    }
1648:                }
1649:                this .eargb = eargb;
1650:                this .pixel = surfaceData.pixelFor(eargb);
1651:            }
1652:
1653:            public void setColor(Color color) {
1654:                if (color == null || color == paint) {
1655:                    return;
1656:                }
1657:                this .paint = foregroundColor = color;
1658:                validateColor();
1659:                if ((eargb >> 24) == -1) {
1660:                    if (paintState == PAINT_OPAQUECOLOR) {
1661:                        return;
1662:                    }
1663:                    paintState = PAINT_OPAQUECOLOR;
1664:                    if (imageComp == CompositeType.SrcOverNoEa) {
1665:                        // special case where compState depends on opacity of paint
1666:                        compositeState = COMP_ISCOPY;
1667:                    }
1668:                } else {
1669:                    if (paintState == PAINT_ALPHACOLOR) {
1670:                        return;
1671:                    }
1672:                    paintState = PAINT_ALPHACOLOR;
1673:                    if (imageComp == CompositeType.SrcOverNoEa) {
1674:                        // special case where compState depends on opacity of paint
1675:                        compositeState = COMP_ALPHA;
1676:                    }
1677:                }
1678:                validFontInfo = false;
1679:                invalidatePipe();
1680:            }
1681:
1682:            /**
1683:             * Sets the background color in this context used for clearing a region.
1684:             * When Graphics2D is constructed for a component, the backgroung color is
1685:             * inherited from the component. Setting the background color in the
1686:             * Graphics2D context only affects the subsequent clearRect() calls and
1687:             * not the background color of the component. To change the background
1688:             * of the component, use appropriate methods of the component.
1689:             * @param color The background color that should be used in
1690:             * subsequent calls to clearRect().
1691:             * @see getBackground
1692:             * @see Graphics.clearRect()
1693:             */
1694:            public void setBackground(Color color) {
1695:                backgroundColor = color;
1696:            }
1697:
1698:            /**
1699:             * Returns the background color used for clearing a region.
1700:             * @see setBackground
1701:             */
1702:            public Color getBackground() {
1703:                return backgroundColor;
1704:            }
1705:
1706:            /**
1707:             * Returns the current Stroke in the Graphics2D state.
1708:             * @see setStroke
1709:             */
1710:            public Stroke getStroke() {
1711:                return stroke;
1712:            }
1713:
1714:            public Rectangle getClipBounds() {
1715:                Rectangle r;
1716:                if (clipState == CLIP_DEVICE) {
1717:                    r = null;
1718:                } else if (transformState <= TRANSFORM_INT_TRANSLATE) {
1719:                    if (usrClip instanceof  Rectangle) {
1720:                        r = new Rectangle((Rectangle) usrClip);
1721:                    } else {
1722:                        r = usrClip.getBounds();
1723:                    }
1724:                    r.translate(-transX, -transY);
1725:                } else {
1726:                    r = getClip().getBounds();
1727:                }
1728:                return r;
1729:            }
1730:
1731:            public Rectangle getClipBounds(Rectangle r) {
1732:                if (clipState != CLIP_DEVICE) {
1733:                    if (transformState <= TRANSFORM_INT_TRANSLATE) {
1734:                        if (usrClip instanceof  Rectangle) {
1735:                            r.setBounds((Rectangle) usrClip);
1736:                        } else {
1737:                            r.setBounds(usrClip.getBounds());
1738:                        }
1739:                        r.translate(-transX, -transY);
1740:                    } else {
1741:                        r.setBounds(getClip().getBounds());
1742:                    }
1743:                } else if (r == null) {
1744:                    throw new NullPointerException("null rectangle parameter");
1745:                }
1746:                return r;
1747:            }
1748:
1749:            public boolean hitClip(int x, int y, int width, int height) {
1750:                if (width <= 0 || height <= 0) {
1751:                    return false;
1752:                }
1753:                if (transformState > TRANSFORM_INT_TRANSLATE) {
1754:                    // Note: Technically the most accurate test would be to
1755:                    // raster scan the parallelogram of the transformed rectangle
1756:                    // and do a span for span hit test against the clip, but for
1757:                    // speed we approximate the test with a bounding box of the
1758:                    // transformed rectangle.  The cost of rasterizing the
1759:                    // transformed rectangle is probably high enough that it is
1760:                    // not worth doing so to save the caller from having to call
1761:                    // a rendering method where we will end up discovering the
1762:                    // same answer in about the same amount of time anyway.
1763:                    // This logic breaks down if this hit test is being performed
1764:                    // on the bounds of a group of shapes in which case it might
1765:                    // be beneficial to be a little more accurate to avoid lots
1766:                    // of subsequent rendering calls.  In either case, this relaxed
1767:                    // test should not be significantly less accurate than the
1768:                    // optimal test for most transforms and so the conservative
1769:                    // answer should not cause too much extra work.
1770:
1771:                    double d[] = { x, y, x + width, y, x, y + height,
1772:                            x + width, y + height };
1773:                    transform.transform(d, 0, d, 0, 4);
1774:                    x = (int) Math.floor(Math.min(Math.min(d[0], d[2]), Math
1775:                            .min(d[4], d[6])));
1776:                    y = (int) Math.floor(Math.min(Math.min(d[1], d[3]), Math
1777:                            .min(d[5], d[7])));
1778:                    width = (int) Math.ceil(Math.max(Math.max(d[0], d[2]), Math
1779:                            .max(d[4], d[6])));
1780:                    height = (int) Math.ceil(Math.max(Math.max(d[1], d[3]),
1781:                            Math.max(d[5], d[7])));
1782:                } else {
1783:                    x += transX;
1784:                    y += transY;
1785:                    width += x;
1786:                    height += y;
1787:                }
1788:                if (!getCompClip()
1789:                        .intersectsQuickCheckXYXY(x, y, width, height)) {
1790:                    return false;
1791:                }
1792:                // REMIND: We could go one step further here and examine the
1793:                // non-rectangular clip shape more closely if there is one.
1794:                // Since the clip has already been rasterized, the performance
1795:                // penalty of doing the scan is probably still within the bounds
1796:                // of a good tradeoff between speed and quality of the answer.
1797:                return true;
1798:            }
1799:
1800:            protected void validateCompClip() {
1801:                int origClipState = clipState;
1802:                if (usrClip == null) {
1803:                    clipState = CLIP_DEVICE;
1804:                    clipRegion = devClip;
1805:                } else if (usrClip instanceof  Rectangle2D) {
1806:                    clipState = CLIP_RECTANGULAR;
1807:                    if (usrClip instanceof  Rectangle) {
1808:                        clipRegion = devClip
1809:                                .getIntersection((Rectangle) usrClip);
1810:                    } else {
1811:                        clipRegion = devClip.getIntersection(usrClip
1812:                                .getBounds());
1813:                    }
1814:                } else {
1815:                    PathIterator cpi = usrClip.getPathIterator(null);
1816:                    int box[] = new int[4];
1817:                    ShapeSpanIterator sr = new ShapeSpanIterator(this , false);
1818:                    try {
1819:                        sr.setOutputArea(devClip);
1820:                        sr.appendPath(cpi);
1821:                        sr.getPathBox(box);
1822:                        Region r = Region.getInstance(box);
1823:                        r.appendSpans(sr);
1824:                        clipRegion = r;
1825:                        clipState = r.isRectangular() ? CLIP_RECTANGULAR
1826:                                : CLIP_SHAPE;
1827:                    } finally {
1828:                        sr.dispose();
1829:                    }
1830:                }
1831:                if (origClipState != clipState
1832:                        && (clipState == CLIP_SHAPE || origClipState == CLIP_SHAPE)) {
1833:                    validFontInfo = false;
1834:                    invalidatePipe();
1835:                }
1836:            }
1837:
1838:            static final int NON_RECTILINEAR_TRANSFORM_MASK = (AffineTransform.TYPE_GENERAL_TRANSFORM | AffineTransform.TYPE_GENERAL_ROTATION);
1839:
1840:            protected Shape transformShape(Shape s) {
1841:                if (s == null) {
1842:                    return null;
1843:                }
1844:                if (transformState > TRANSFORM_INT_TRANSLATE) {
1845:                    return transformShape(transform, s);
1846:                } else {
1847:                    return transformShape(transX, transY, s);
1848:                }
1849:            }
1850:
1851:            public Shape untransformShape(Shape s) {
1852:                if (s == null) {
1853:                    return null;
1854:                }
1855:                if (transformState > TRANSFORM_INT_TRANSLATE) {
1856:                    try {
1857:                        return transformShape(transform.createInverse(), s);
1858:                    } catch (NoninvertibleTransformException e) {
1859:                        return null;
1860:                    }
1861:                } else {
1862:                    return transformShape(-transX, -transY, s);
1863:                }
1864:            }
1865:
1866:            protected static Shape transformShape(int tx, int ty, Shape s) {
1867:                if (s == null) {
1868:                    return null;
1869:                }
1870:
1871:                if (s instanceof  Rectangle) {
1872:                    Rectangle r = s.getBounds();
1873:                    r.translate(tx, ty);
1874:                    return r;
1875:                }
1876:                if (s instanceof  Rectangle2D) {
1877:                    Rectangle2D rect = (Rectangle2D) s;
1878:                    return new Rectangle2D.Double(rect.getX() + tx, rect.getY()
1879:                            + ty, rect.getWidth(), rect.getHeight());
1880:                }
1881:
1882:                if (tx == 0 && ty == 0) {
1883:                    return cloneShape(s);
1884:                }
1885:
1886:                AffineTransform mat = AffineTransform.getTranslateInstance(tx,
1887:                        ty);
1888:                return mat.createTransformedShape(s);
1889:            }
1890:
1891:            protected static Shape transformShape(AffineTransform tx, Shape clip) {
1892:                if (clip == null) {
1893:                    return null;
1894:                }
1895:
1896:                if (clip instanceof  Rectangle2D
1897:                        && (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0) {
1898:                    Rectangle2D rect = (Rectangle2D) clip;
1899:                    double matrix[] = new double[4];
1900:                    matrix[0] = rect.getX();
1901:                    matrix[1] = rect.getY();
1902:                    matrix[2] = matrix[0] + rect.getWidth();
1903:                    matrix[3] = matrix[1] + rect.getHeight();
1904:                    tx.transform(matrix, 0, matrix, 0, 2);
1905:                    rect = new Rectangle2D.Float();
1906:                    rect.setFrameFromDiagonal(matrix[0], matrix[1], matrix[2],
1907:                            matrix[3]);
1908:                    return rect;
1909:                }
1910:
1911:                if (tx.isIdentity()) {
1912:                    return cloneShape(clip);
1913:                }
1914:
1915:                return tx.createTransformedShape(clip);
1916:            }
1917:
1918:            public void clipRect(int x, int y, int w, int h) {
1919:                clip(new Rectangle(x, y, w, h));
1920:            }
1921:
1922:            public void setClip(int x, int y, int w, int h) {
1923:                setClip(new Rectangle(x, y, w, h));
1924:            }
1925:
1926:            public Shape getClip() {
1927:                return untransformShape(usrClip);
1928:            }
1929:
1930:            public void setClip(Shape sh) {
1931:                usrClip = transformShape(sh);
1932:                validateCompClip();
1933:            }
1934:
1935:            /**
1936:             * Intersects the current clip with the specified Path and sets the
1937:             * current clip to the resulting intersection. The clip is transformed
1938:             * with the current transform in the Graphics2D state before being
1939:             * intersected with the current clip. This method is used to make the
1940:             * current clip smaller. To make the clip larger, use any setClip method.
1941:             * @param p The Path to be intersected with the current clip.
1942:             */
1943:            public void clip(Shape s) {
1944:                s = transformShape(s);
1945:                if (usrClip != null) {
1946:                    s = intersectShapes(usrClip, s, true, true);
1947:                }
1948:                usrClip = s;
1949:                validateCompClip();
1950:            }
1951:
1952:            public void setPaintMode() {
1953:                setComposite(AlphaComposite.SrcOver);
1954:            }
1955:
1956:            public void setXORMode(Color c) {
1957:                if (c == null) {
1958:                    throw new IllegalArgumentException("null XORColor");
1959:                }
1960:                setComposite(new XORComposite(c, surfaceData));
1961:            }
1962:
1963:            Blit lastCAblit;
1964:            Composite lastCAcomp;
1965:
1966:            public void copyArea(int x, int y, int w, int h, int dx, int dy) {
1967:                try {
1968:                    doCopyArea(x, y, w, h, dx, dy);
1969:                } catch (InvalidPipeException e) {
1970:                    revalidateAll();
1971:                    try {
1972:                        doCopyArea(x, y, w, h, dx, dy);
1973:                    } catch (InvalidPipeException e2) {
1974:                        // Still catching the exception; we are not yet ready to
1975:                        // validate the surfaceData correctly.  Fail for now and 
1976:                        // try again next time around.
1977:                    }
1978:                } finally {
1979:                    surfaceData.markDirty();
1980:                }
1981:            }
1982:
1983:            private void doCopyArea(int x, int y, int w, int h, int dx, int dy) {
1984:                if (w <= 0 || h <= 0) {
1985:                    return;
1986:                }
1987:                SurfaceData theData = surfaceData;
1988:                if (theData.copyArea(this , x, y, w, h, dx, dy)) {
1989:                    return;
1990:                }
1991:                if (transformState >= TRANSFORM_TRANSLATESCALE) {
1992:                    throw new InternalError(
1993:                            "transformed copyArea not implemented yet");
1994:                }
1995:                // REMIND: This method does not deal with missing data from the
1996:                // source object (i.e. it does not send exposure events...)
1997:
1998:                Region clip = getCompClip();
1999:
2000:                Composite comp = composite;
2001:                if (lastCAcomp != comp) {
2002:                    SurfaceType dsttype = theData.getSurfaceType();
2003:                    CompositeType comptype = imageComp;
2004:                    if (CompositeType.SrcOverNoEa.equals(comptype)
2005:                            && theData.getTransparency() == Transparency.OPAQUE) {
2006:                        comptype = CompositeType.SrcNoEa;
2007:                    }
2008:                    lastCAblit = Blit.locate(dsttype, comptype, dsttype);
2009:                    lastCAcomp = comp;
2010:                }
2011:
2012:                x += transX;
2013:                y += transY;
2014:
2015:                Blit ob = lastCAblit;
2016:                if (dy == 0 && dx > 0 && dx < w) {
2017:                    while (w > 0) {
2018:                        int partW = Math.min(w, dx);
2019:                        w -= partW;
2020:                        int sx = x + w;
2021:                        ob.Blit(theData, theData, comp, clip, sx, y, sx + dx, y
2022:                                + dy, partW, h);
2023:                    }
2024:                    return;
2025:                }
2026:                if (dy > 0 && dy < h && dx > -w && dx < w) {
2027:                    while (h > 0) {
2028:                        int partH = Math.min(h, dy);
2029:                        h -= partH;
2030:                        int sy = y + h;
2031:                        ob.Blit(theData, theData, comp, clip, x, sy, x + dx, sy
2032:                                + dy, w, partH);
2033:                    }
2034:                    return;
2035:                }
2036:                ob.Blit(theData, theData, comp, clip, x, y, x + dx, y + dy, w,
2037:                        h);
2038:            }
2039:
2040:            /*
2041:            public void XcopyArea(int x, int y, int w, int h, int dx, int dy) {
2042:            Rectangle rect = new Rectangle(x, y, w, h);
2043:            rect = transformBounds(rect, transform);
2044:                Point2D    point = new Point2D.Float(dx, dy);
2045:                Point2D    root  = new Point2D.Float(0, 0);
2046:                point = transform.transform(point, point);
2047:                root  = transform.transform(root, root);
2048:                int fdx = (int)(point.getX()-root.getX());
2049:                int fdy = (int)(point.getY()-root.getY());
2050:
2051:                Rectangle r = getCompBounds().intersection(rect.getBounds());
2052:
2053:                if (r.isEmpty()) {
2054:                    return;
2055:                }
2056:
2057:                // Begin Rasterizer for Clip Shape
2058:                boolean skipClip = true;
2059:                byte[] clipAlpha = null;
2060:
2061:                if (clipState == CLIP_SHAPE) {
2062:
2063:                int box[] = new int[4];
2064:
2065:                clipRegion.getBounds(box);
2066:                Rectangle devR = new Rectangle(box[0], box[1],
2067:            				   box[2] - box[0],
2068:            				   box[3] - box[1]);
2069:                if (!devR.isEmpty()) {
2070:            	OutputManager mgr = getOutputManager();
2071:            	RegionIterator ri = clipRegion.getIterator();
2072:            	while (ri.nextYRange(box)) {
2073:            	    int spany = box[1];
2074:            	    int spanh = box[3] - spany;
2075:            	    while (ri.nextXBand(box)) {
2076:            		int spanx = box[0];
2077:            		int spanw = box[2] - spanx;
2078:            		mgr.copyArea(this, null,
2079:            			     spanw, 0,
2080:            			     spanx, spany,
2081:            			     spanw, spanh,
2082:            			     fdx, fdy,
2083:            			     null);
2084:            	    }
2085:            	}
2086:                }
2087:                return;
2088:            }
2089:                // End Rasterizer for Clip Shape
2090:
2091:                getOutputManager().copyArea(this, null,
2092:            			    r.width, 0,
2093:            			    r.x, r.y, r.width,
2094:            			    r.height, fdx, fdy,
2095:            			    null);
2096:            }
2097:             */
2098:
2099:            public void drawLine(int x1, int y1, int x2, int y2) {
2100:                try {
2101:                    drawpipe.drawLine(this , x1, y1, x2, y2);
2102:                } catch (InvalidPipeException e) {
2103:                    revalidateAll();
2104:                    try {
2105:                        drawpipe.drawLine(this , x1, y1, x2, y2);
2106:                    } catch (InvalidPipeException e2) {
2107:                        // Still catching the exception; we are not yet ready to
2108:                        // validate the surfaceData correctly.  Fail for now and 
2109:                        // try again next time around.
2110:                    }
2111:                } finally {
2112:                    surfaceData.markDirty();
2113:                }
2114:            }
2115:
2116:            public void drawRoundRect(int x, int y, int w, int h, int arcW,
2117:                    int arcH) {
2118:                try {
2119:                    drawpipe.drawRoundRect(this , x, y, w, h, arcW, arcH);
2120:                } catch (InvalidPipeException e) {
2121:                    revalidateAll();
2122:                    try {
2123:                        drawpipe.drawRoundRect(this , x, y, w, h, arcW, arcH);
2124:                    } catch (InvalidPipeException e2) {
2125:                        // Still catching the exception; we are not yet ready to
2126:                        // validate the surfaceData correctly.  Fail for now and 
2127:                        // try again next time around.
2128:                    }
2129:                } finally {
2130:                    surfaceData.markDirty();
2131:                }
2132:            }
2133:
2134:            public void fillRoundRect(int x, int y, int w, int h, int arcW,
2135:                    int arcH) {
2136:                try {
2137:                    fillpipe.fillRoundRect(this , x, y, w, h, arcW, arcH);
2138:                } catch (InvalidPipeException e) {
2139:                    revalidateAll();
2140:                    try {
2141:                        fillpipe.fillRoundRect(this , x, y, w, h, arcW, arcH);
2142:                    } catch (InvalidPipeException e2) {
2143:                        // Still catching the exception; we are not yet ready to
2144:                        // validate the surfaceData correctly.  Fail for now and 
2145:                        // try again next time around.
2146:                    }
2147:                } finally {
2148:                    surfaceData.markDirty();
2149:                }
2150:            }
2151:
2152:            public void drawOval(int x, int y, int w, int h) {
2153:                try {
2154:                    drawpipe.drawOval(this , x, y, w, h);
2155:                } catch (InvalidPipeException e) {
2156:                    revalidateAll();
2157:                    try {
2158:                        drawpipe.drawOval(this , x, y, w, h);
2159:                    } catch (InvalidPipeException e2) {
2160:                        // Still catching the exception; we are not yet ready to
2161:                        // validate the surfaceData correctly.  Fail for now and 
2162:                        // try again next time around.
2163:                    }
2164:                } finally {
2165:                    surfaceData.markDirty();
2166:                }
2167:            }
2168:
2169:            public void fillOval(int x, int y, int w, int h) {
2170:                try {
2171:                    fillpipe.fillOval(this , x, y, w, h);
2172:                } catch (InvalidPipeException e) {
2173:                    revalidateAll();
2174:                    try {
2175:                        fillpipe.fillOval(this , x, y, w, h);
2176:                    } catch (InvalidPipeException e2) {
2177:                        // Still catching the exception; we are not yet ready to
2178:                        // validate the surfaceData correctly.  Fail for now and 
2179:                        // try again next time around.
2180:                    }
2181:                } finally {
2182:                    surfaceData.markDirty();
2183:                }
2184:            }
2185:
2186:            public void drawArc(int x, int y, int w, int h, int startAngl,
2187:                    int arcAngl) {
2188:                try {
2189:                    drawpipe.drawArc(this , x, y, w, h, startAngl, arcAngl);
2190:                } catch (InvalidPipeException e) {
2191:                    revalidateAll();
2192:                    try {
2193:                        drawpipe.drawArc(this , x, y, w, h, startAngl, arcAngl);
2194:                    } catch (InvalidPipeException e2) {
2195:                        // Still catching the exception; we are not yet ready to
2196:                        // validate the surfaceData correctly.  Fail for now and 
2197:                        // try again next time around.
2198:                    }
2199:                } finally {
2200:                    surfaceData.markDirty();
2201:                }
2202:            }
2203:
2204:            public void fillArc(int x, int y, int w, int h, int startAngl,
2205:                    int arcAngl) {
2206:                try {
2207:                    fillpipe.fillArc(this , x, y, w, h, startAngl, arcAngl);
2208:                } catch (InvalidPipeException e) {
2209:                    revalidateAll();
2210:                    try {
2211:                        fillpipe.fillArc(this , x, y, w, h, startAngl, arcAngl);
2212:                    } catch (InvalidPipeException e2) {
2213:                        // Still catching the exception; we are not yet ready to
2214:                        // validate the surfaceData correctly.  Fail for now and 
2215:                        // try again next time around.
2216:                    }
2217:                } finally {
2218:                    surfaceData.markDirty();
2219:                }
2220:            }
2221:
2222:            public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
2223:                try {
2224:                    drawpipe.drawPolyline(this , xPoints, yPoints, nPoints);
2225:                } catch (InvalidPipeException e) {
2226:                    revalidateAll();
2227:                    try {
2228:                        drawpipe.drawPolyline(this , xPoints, yPoints, nPoints);
2229:                    } catch (InvalidPipeException e2) {
2230:                        // Still catching the exception; we are not yet ready to
2231:                        // validate the surfaceData correctly.  Fail for now and 
2232:                        // try again next time around.
2233:                    }
2234:                } finally {
2235:                    surfaceData.markDirty();
2236:                }
2237:            }
2238:
2239:            public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
2240:                try {
2241:                    drawpipe.drawPolygon(this , xPoints, yPoints, nPoints);
2242:                } catch (InvalidPipeException e) {
2243:                    revalidateAll();
2244:                    try {
2245:                        drawpipe.drawPolygon(this , xPoints, yPoints, nPoints);
2246:                    } catch (InvalidPipeException e2) {
2247:                        // Still catching the exception; we are not yet ready to
2248:                        // validate the surfaceData correctly.  Fail for now and 
2249:                        // try again next time around.
2250:                    }
2251:                } finally {
2252:                    surfaceData.markDirty();
2253:                }
2254:            }
2255:
2256:            public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
2257:                try {
2258:                    fillpipe.fillPolygon(this , xPoints, yPoints, nPoints);
2259:                } catch (InvalidPipeException e) {
2260:                    revalidateAll();
2261:                    try {
2262:                        fillpipe.fillPolygon(this , xPoints, yPoints, nPoints);
2263:                    } catch (InvalidPipeException e2) {
2264:                        // Still catching the exception; we are not yet ready to
2265:                        // validate the surfaceData correctly.  Fail for now and 
2266:                        // try again next time around.
2267:                    }
2268:                } finally {
2269:                    surfaceData.markDirty();
2270:                }
2271:            }
2272:
2273:            public void drawRect(int x, int y, int w, int h) {
2274:                try {
2275:                    drawpipe.drawRect(this , x, y, w, h);
2276:                } catch (InvalidPipeException e) {
2277:                    revalidateAll();
2278:                    try {
2279:                        drawpipe.drawRect(this , x, y, w, h);
2280:                    } catch (InvalidPipeException e2) {
2281:                        // Still catching the exception; we are not yet ready to
2282:                        // validate the surfaceData correctly.  Fail for now and 
2283:                        // try again next time around.
2284:                    }
2285:                } finally {
2286:                    surfaceData.markDirty();
2287:                }
2288:            }
2289:
2290:            public void fillRect(int x, int y, int w, int h) {
2291:                try {
2292:                    fillpipe.fillRect(this , x, y, w, h);
2293:                } catch (InvalidPipeException e) {
2294:                    revalidateAll();
2295:                    try {
2296:                        fillpipe.fillRect(this , x, y, w, h);
2297:                    } catch (InvalidPipeException e2) {
2298:                        // Still catching the exception; we are not yet ready to
2299:                        // validate the surfaceData correctly.  Fail for now and 
2300:                        // try again next time around.
2301:                    }
2302:                } finally {
2303:                    surfaceData.markDirty();
2304:                }
2305:            }
2306:
2307:            private void revalidateAll() {
2308:                try {
2309:                    // REMIND: This locking needs to be done around the
2310:                    // caller of this method so that the pipe stays valid
2311:                    // long enough to call the new primitive.
2312:                    // REMIND: No locking yet in screen SurfaceData objects!
2313:                    // surfaceData.lock();
2314:                    surfaceData = surfaceData.getReplacement();
2315:                    if (surfaceData == null) {
2316:                        surfaceData = NullSurfaceData.theInstance;
2317:                    }
2318:
2319:                    // this will recalculate the composite clip
2320:                    setDevClip(surfaceData.getBounds());
2321:
2322:                    if (paintState <= PAINT_ALPHACOLOR) {
2323:                        validateColor();
2324:                    }
2325:                    if (composite instanceof  XORComposite) {
2326:                        Color c = ((XORComposite) composite).getXorColor();
2327:                        setComposite(new XORComposite(c, surfaceData));
2328:                    }
2329:                    validatePipe();
2330:                } finally {
2331:                    // REMIND: No locking yet in screen SurfaceData objects!
2332:                    // surfaceData.unlock();
2333:                }
2334:            }
2335:
2336:            public void clearRect(int x, int y, int w, int h) {
2337:                // REMIND: has some "interesting" consequences if threads are
2338:                // not synchronized
2339:                Composite c = composite;
2340:                Paint p = paint;
2341:                setComposite(AlphaComposite.Src);
2342:                setColor(getBackground());
2343:                validatePipe();
2344:                fillRect(x, y, w, h);
2345:                setPaint(p);
2346:                setComposite(c);
2347:            }
2348:
2349:            /**
2350:             * Strokes the outline of a Path using the settings of the current
2351:             * graphics state.  The rendering attributes applied include the
2352:             * clip, transform, paint or color, composite and stroke attributes.
2353:             * @param p The path to be drawn.
2354:             * @see #setStroke
2355:             * @see #setPaint
2356:             * @see java.awt.Graphics#setColor
2357:             * @see #transform
2358:             * @see #setTransform
2359:             * @see #clip
2360:             * @see #setClip
2361:             * @see #setComposite
2362:             */
2363:            public void draw(Shape s) {
2364:                try {
2365:                    shapepipe.draw(this , s);
2366:                } catch (InvalidPipeException e) {
2367:                    revalidateAll();
2368:                    try {
2369:                        shapepipe.draw(this , s);
2370:                    } catch (InvalidPipeException e2) {
2371:                        // Still catching the exception; we are not yet ready to
2372:                        // validate the surfaceData correctly.  Fail for now and 
2373:                        // try again next time around.
2374:                    }
2375:                } finally {
2376:                    surfaceData.markDirty();
2377:                }
2378:            }
2379:
2380:            /**
2381:             * Fills the interior of a Path using the settings of the current
2382:             * graphics state. The rendering attributes applied include the
2383:             * clip, transform, paint or color, and composite.
2384:             * @see #setPaint
2385:             * @see java.awt.Graphics#setColor
2386:             * @see #transform
2387:             * @see #setTransform
2388:             * @see #setComposite
2389:             * @see #clip
2390:             * @see #setClip
2391:             */
2392:            public void fill(Shape s) {
2393:                try {
2394:                    shapepipe.fill(this , s);
2395:                } catch (InvalidPipeException e) {
2396:                    revalidateAll();
2397:                    try {
2398:                        shapepipe.fill(this , s);
2399:                    } catch (InvalidPipeException e2) {
2400:                        // Still catching the exception; we are not yet ready to
2401:                        // validate the surfaceData correctly.  Fail for now and 
2402:                        // try again next time around.
2403:                    }
2404:                } finally {
2405:                    surfaceData.markDirty();
2406:                }
2407:            }
2408:
2409:            /**
2410:             * Returns true if the given AffineTransform is an integer
2411:             * translation.
2412:             */
2413:            private static boolean isIntegerTranslation(AffineTransform xform) {
2414:                if (xform.isIdentity()) {
2415:                    return true;
2416:                }
2417:                if (xform.getType() == AffineTransform.TYPE_TRANSLATION) {
2418:                    double tx = xform.getTranslateX();
2419:                    double ty = xform.getTranslateY();
2420:                    return (tx == (int) tx && ty == (int) ty);
2421:                }
2422:                return false;
2423:            }
2424:
2425:            /**
2426:             * Returns the index of the tile corresponding to the supplied position
2427:             * given the tile grid offset and size along the same axis.
2428:             */
2429:            private static int getTileIndex(int p, int tileGridOffset,
2430:                    int tileSize) {
2431:                p -= tileGridOffset;
2432:                if (p < 0) {
2433:                    p += 1 - tileSize; // force round to -infinity (ceiling)
2434:                }
2435:                return p / tileSize;
2436:            }
2437:
2438:            /**
2439:             * Returns a rectangle in image coordinates that may be required
2440:             * in order to draw the given image into the given clipping region
2441:             * through a pair of AffineTransforms.  In addition, horizontal and
2442:             * vertical padding factors for antialising and interpolation may
2443:             * be used.
2444:             */
2445:            private static Rectangle getImageRegion(RenderedImage img,
2446:                    Region compClip, AffineTransform transform,
2447:                    AffineTransform xform, int padX, int padY) {
2448:                Rectangle imageRect = new Rectangle(img.getMinX(), img
2449:                        .getMinY(), img.getWidth(), img.getHeight());
2450:
2451:                Rectangle result = null;
2452:                try {
2453:                    double p[] = new double[8];
2454:                    p[0] = p[2] = compClip.getLoX();
2455:                    p[4] = p[6] = compClip.getHiX();
2456:                    p[1] = p[5] = compClip.getLoY();
2457:                    p[3] = p[7] = compClip.getHiY();
2458:
2459:                    // Inverse transform the output bounding rect
2460:                    transform.inverseTransform(p, 0, p, 0, 4);
2461:                    xform.inverseTransform(p, 0, p, 0, 4);
2462:
2463:                    // Determine a bounding box for the inverse transformed region
2464:                    double x0, x1, y0, y1;
2465:                    x0 = x1 = p[0];
2466:                    y0 = y1 = p[1];
2467:
2468:                    for (int i = 2; i < 8;) {
2469:                        double pt = p[i++];
2470:                        if (pt < x0) {
2471:                            x0 = pt;
2472:                        } else if (pt > x1) {
2473:                            x1 = pt;
2474:                        }
2475:                        pt = p[i++];
2476:                        if (pt < y0) {
2477:                            y0 = pt;
2478:                        } else if (pt > y1) {
2479:                            y1 = pt;
2480:                        }
2481:                    }
2482:
2483:                    // This is padding for anti-aliasing and such.  It may
2484:                    // be more than is needed.
2485:                    int x = (int) x0 - padX;
2486:                    int w = (int) (x1 - x0 + 2 * padX);
2487:                    int y = (int) y0 - padY;
2488:                    int h = (int) (y1 - y0 + 2 * padY);
2489:
2490:                    Rectangle clipRect = new Rectangle(x, y, w, h);
2491:                    result = clipRect.intersection(imageRect);
2492:                } catch (NoninvertibleTransformException nte) {
2493:                    // Worst case bounds are the bounds of the image.
2494:                    result = imageRect;
2495:                }
2496:
2497:                return result;
2498:            }
2499:
2500:            /**
2501:             * Draws an image, applying a transform from image space into user space
2502:             * before drawing.
2503:             * The transformation from user space into device space is done with
2504:             * the current transform in the Graphics2D.
2505:             * The given transformation is applied to the image before the
2506:             * transform attribute in the Graphics2D state is applied.
2507:             * The rendering attributes applied include the clip, transform,
2508:             * and composite attributes. Note that the result is
2509:             * undefined, if the given transform is noninvertible.
2510:             * @param img The image to be drawn. Does nothing if img is null.
2511:             * @param xform The transformation from image space into user space.
2512:             * @see #transform
2513:             * @see #setTransform
2514:             * @see #setComposite
2515:             * @see #clip
2516:             * @see #setClip
2517:             */
2518:            public void drawRenderedImage(RenderedImage img,
2519:                    AffineTransform xform) {
2520:
2521:                if (img == null) {
2522:                    return;
2523:                }
2524:
2525:                // BufferedImage case: use a simple drawImage call
2526:                if (img instanceof  BufferedImage) {
2527:                    BufferedImage bufImg = (BufferedImage) img;
2528:                    drawImage(bufImg, xform, null);
2529:                    return;
2530:                }
2531:
2532:                // transformState tracks the state of transform and
2533:                // transX, transY contain the integer casts of the
2534:                // translation factors
2535:                boolean isIntegerTranslate = (transformState <= TRANSFORM_INT_TRANSLATE)
2536:                        && isIntegerTranslation(xform);
2537:
2538:                // Include padding for interpolation/antialiasing if necessary
2539:                int pad = isIntegerTranslate ? 0 : 3;
2540:
2541:                // Determine the region of the image that may contribute to
2542:                // the clipped drawing area
2543:                Rectangle region = getImageRegion(img, getCompClip(),
2544:                        transform, xform, pad, pad);
2545:                if (region.width <= 0 || region.height <= 0) {
2546:                    return;
2547:                }
2548:
2549:                // Attempt to optimize integer translation of tiled images.
2550:                // Although theoretically we are O.K. if the concatenation of
2551:                // the user transform and the device transform is an integer
2552:                // translation, we'll play it safe and only optimize the case
2553:                // where both are integer translations.
2554:                if (isIntegerTranslate) {
2555:                    // Use optimized code
2556:                    // Note that drawTranslatedRenderedImage calls copyImage
2557:                    // which takes the user space to device space transform into
2558:                    // account, but we need to provide the image space to user space
2559:                    // translations.
2560:
2561:                    drawTranslatedRenderedImage(img, region, (int) xform
2562:                            .getTranslateX(), (int) xform.getTranslateY());
2563:                    return;
2564:                }
2565:
2566:                // General case: cobble the necessary region into a single Raster
2567:                Raster raster = img.getData(region);
2568:
2569:                // Make a new Raster with the same contents as raster
2570:                // but starting at (0, 0).  This raster is thus in the same
2571:                // coordinate system as the SampleModel of the original raster.
2572:                WritableRaster wRaster = Raster.createWritableRaster(raster
2573:                        .getSampleModel(), raster.getDataBuffer(), null);
2574:
2575:                // If the original raster was in a different coordinate
2576:                // system than its SampleModel, we need to perform an
2577:                // additional translation in order to get the (minX, minY)
2578:                // pixel of raster to be pixel (0, 0) of wRaster.  We also
2579:                // have to have the correct width and height.
2580:                int minX = raster.getMinX();
2581:                int minY = raster.getMinY();
2582:                int width = raster.getWidth();
2583:                int height = raster.getHeight();
2584:                int px = minX - raster.getSampleModelTranslateX();
2585:                int py = minY - raster.getSampleModelTranslateY();
2586:                if (px != 0 || py != 0 || width != wRaster.getWidth()
2587:                        || height != wRaster.getHeight()) {
2588:                    wRaster = wRaster.createWritableChild(px, py, width,
2589:                            height, 0, 0, null);
2590:                }
2591:
2592:                // Now we have a BufferedImage starting at (0, 0)
2593:                // with the same contents that started at (minX, minY)
2594:                // in raster.  So we must draw the BufferedImage with a
2595:                // translation of (minX, minY).
2596:                AffineTransform transXform = (AffineTransform) xform.clone();
2597:                transXform.translate(minX, minY);
2598:
2599:                ColorModel cm = img.getColorModel();
2600:                BufferedImage bufImg = new BufferedImage(cm, wRaster, cm
2601:                        .isAlphaPremultiplied(), null);
2602:                drawImage(bufImg, transXform, null);
2603:            }
2604:
2605:            /**
2606:             * Intersects <code>destRect</code> with <code>clip</code> and
2607:             * overwrites <code>destRect</code> with the result.
2608:             * Returns false if the intersection was empty, true otherwise.
2609:             */
2610:            private boolean clipTo(Rectangle destRect, Rectangle clip) {
2611:                int x1 = Math.max(destRect.x, clip.x);
2612:                int x2 = Math.min(destRect.x + destRect.width, clip.x
2613:                        + clip.width);
2614:                int y1 = Math.max(destRect.y, clip.y);
2615:                int y2 = Math.min(destRect.y + destRect.height, clip.y
2616:                        + clip.height);
2617:                if (((x2 - x1) < 0) || ((y2 - y1) < 0)) {
2618:                    destRect.width = -1; // Set both just to be safe
2619:                    destRect.height = -1;
2620:                    return false;
2621:                } else {
2622:                    destRect.x = x1;
2623:                    destRect.y = y1;
2624:                    destRect.width = x2 - x1;
2625:                    destRect.height = y2 - y1;
2626:                    return true;
2627:                }
2628:            }
2629:
2630:            /**
2631:             * Draw a portion of a RenderedImage tile-by-tile with a given
2632:             * integer image to user space translation.  The user to
2633:             * device transform must also be an integer translation.
2634:             */
2635:            private void drawTranslatedRenderedImage(RenderedImage img,
2636:                    Rectangle region, int i2uTransX, int i2uTransY) {
2637:                // Cache tile grid info
2638:                int tileGridXOffset = img.getTileGridXOffset();
2639:                int tileGridYOffset = img.getTileGridYOffset();
2640:                int tileWidth = img.getTileWidth();
2641:                int tileHeight = img.getTileHeight();
2642:
2643:                // Determine the tile index extrema in each direction
2644:                int minTileX = getTileIndex(region.x, tileGridXOffset,
2645:                        tileWidth);
2646:                int minTileY = getTileIndex(region.y, tileGridYOffset,
2647:                        tileHeight);
2648:                int maxTileX = getTileIndex(region.x + region.width - 1,
2649:                        tileGridXOffset, tileWidth);
2650:                int maxTileY = getTileIndex(region.y + region.height - 1,
2651:                        tileGridYOffset, tileHeight);
2652:
2653:                // Create a single ColorModel to use for all BufferedImages
2654:                ColorModel colorModel = img.getColorModel();
2655:
2656:                // Reuse the same Rectangle for each iteration
2657:                Rectangle tileRect = new Rectangle();
2658:
2659:                for (int ty = minTileY; ty <= maxTileY; ty++) {
2660:                    for (int tx = minTileX; tx <= maxTileX; tx++) {
2661:                        // Get the current tile.
2662:                        Raster raster = img.getTile(tx, ty);
2663:
2664:                        // Fill in tileRect with the tile bounds
2665:                        tileRect.x = tx * tileWidth + tileGridXOffset;
2666:                        tileRect.y = ty * tileHeight + tileGridYOffset;
2667:                        tileRect.width = tileWidth;
2668:                        tileRect.height = tileHeight;
2669:
2670:                        // Clip the tile against the image bounds and
2671:                        // backwards mapped clip region
2672:                        // The result can't be empty
2673:                        clipTo(tileRect, region);
2674:
2675:                        // Create a WritableRaster containing the tile
2676:                        WritableRaster wRaster = null;
2677:                        if (raster instanceof  WritableRaster) {
2678:                            wRaster = (WritableRaster) raster;
2679:                        } else {
2680:                            // Create a WritableRaster in the same coordinate system
2681:                            // as the original raster.
2682:                            wRaster = Raster.createWritableRaster(raster
2683:                                    .getSampleModel(), raster.getDataBuffer(),
2684:                                    null);
2685:                        }
2686:
2687:                        // Translate wRaster to start at (0, 0) and to contain
2688:                        // only the relevent portion of the tile
2689:                        wRaster = wRaster.createWritableChild(tileRect.x,
2690:                                tileRect.y, tileRect.width, tileRect.height, 0,
2691:                                0, null);
2692:
2693:                        // Wrap wRaster in a BufferedImage
2694:                        BufferedImage bufImg = new BufferedImage(colorModel,
2695:                                wRaster, colorModel.isAlphaPremultiplied(),
2696:                                null);
2697:                        // Now we have a BufferedImage starting at (0, 0) that
2698:                        // represents data from a Raster starting at
2699:                        // (tileRect.x, tileRect.y).  Additionally, it needs
2700:                        // to be translated by (i2uTransX, i2uTransY).  We call
2701:                        // copyImage to draw just the region of interest
2702:                        // without needing to create a child image.
2703:                        copyImage(bufImg, tileRect.x + i2uTransX, tileRect.y
2704:                                + i2uTransY, 0, 0, tileRect.width,
2705:                                tileRect.height, null, null);
2706:                    }
2707:                }
2708:            }
2709:
2710:            public void drawRenderableImage(RenderableImage img,
2711:                    AffineTransform xform) {
2712:
2713:                if (img == null) {
2714:                    return;
2715:                }
2716:
2717:                AffineTransform pipeTransform = transform;
2718:                AffineTransform concatTransform = new AffineTransform(xform);
2719:                concatTransform.concatenate(pipeTransform);
2720:                AffineTransform reverseTransform;
2721:
2722:                RenderContext rc = new RenderContext(concatTransform);
2723:
2724:                try {
2725:                    reverseTransform = pipeTransform.createInverse();
2726:                } catch (NoninvertibleTransformException nte) {
2727:                    rc = new RenderContext(pipeTransform);
2728:                    reverseTransform = new AffineTransform();
2729:                }
2730:
2731:                RenderedImage rendering = img.createRendering(rc);
2732:                drawRenderedImage(rendering, reverseTransform);
2733:            }
2734:
2735:            /*
2736:             * Transform the bounding box of the BufferedImage
2737:             */
2738:            protected Rectangle transformBounds(Rectangle rect,
2739:                    AffineTransform tx) {
2740:                if (tx.isIdentity()) {
2741:                    return rect;
2742:                }
2743:
2744:                Shape s = transformShape(tx, rect);
2745:                return s.getBounds();
2746:            }
2747:
2748:            // text rendering methods
2749:            public void drawString(String str, int x, int y) {
2750:                if (str == null) {
2751:                    throw new NullPointerException("String is null");
2752:                }
2753:
2754:                if (font.hasLayoutAttributes()) {
2755:                    new TextLayout(str, font, getFontRenderContext()).draw(
2756:                            this , x, y);
2757:                    return;
2758:                }
2759:
2760:                try {
2761:                    textpipe.drawString(this , str, x, y);
2762:                } catch (InvalidPipeException e) {
2763:                    revalidateAll();
2764:                    try {
2765:                        textpipe.drawString(this , str, x, y);
2766:                    } catch (InvalidPipeException e2) {
2767:                        // Still catching the exception; we are not yet ready to
2768:                        // validate the surfaceData correctly.  Fail for now and 
2769:                        // try again next time around.
2770:                    }
2771:                } finally {
2772:                    surfaceData.markDirty();
2773:                }
2774:            }
2775:
2776:            public void drawString(String str, float x, float y) {
2777:                if (str == null) {
2778:                    throw new NullPointerException("String is null");
2779:                }
2780:
2781:                if (font.hasLayoutAttributes()) {
2782:                    new TextLayout(str, font, getFontRenderContext()).draw(
2783:                            this , x, y);
2784:                    return;
2785:                }
2786:
2787:                try {
2788:                    textpipe.drawString(this , str, x, y);
2789:                } catch (InvalidPipeException e) {
2790:                    revalidateAll();
2791:                    try {
2792:                        textpipe.drawString(this , str, x, y);
2793:                    } catch (InvalidPipeException e2) {
2794:                        // Still catching the exception; we are not yet ready to
2795:                        // validate the surfaceData correctly.	Fail for now and
2796:                        // try again next time around.
2797:                    }
2798:                } finally {
2799:                    surfaceData.markDirty();
2800:                }
2801:            }
2802:
2803:            public void drawString(AttributedCharacterIterator iterator, int x,
2804:                    int y) {
2805:                if (iterator == null) {
2806:                    throw new NullPointerException(
2807:                            "AttributedCharacterIterator is null");
2808:                }
2809:                TextLayout tl = new TextLayout(iterator, getFontRenderContext());
2810:                tl.draw(this , (float) x, (float) y);
2811:            }
2812:
2813:            public void drawString(AttributedCharacterIterator iterator,
2814:                    float x, float y) {
2815:                if (iterator == null) {
2816:                    throw new NullPointerException(
2817:                            "AttributedCharacterIterator is null");
2818:                }
2819:                TextLayout tl = new TextLayout(iterator, getFontRenderContext());
2820:                tl.draw(this , x, y);
2821:            }
2822:
2823:            public void drawGlyphVector(GlyphVector gv, float x, float y) {
2824:                if (gv == null) {
2825:                    throw new NullPointerException("GlyphVector is null");
2826:                }
2827:
2828:                try {
2829:                    textpipe.drawGlyphVector(this , gv, x, y);
2830:                } catch (InvalidPipeException e) {
2831:                    revalidateAll();
2832:                    try {
2833:                        textpipe.drawGlyphVector(this , gv, x, y);
2834:                    } catch (InvalidPipeException e2) {
2835:                        // Still catching the exception; we are not yet ready to
2836:                        // validate the surfaceData correctly.  Fail for now and 
2837:                        // try again next time around.
2838:                    }
2839:                } finally {
2840:                    surfaceData.markDirty();
2841:                }
2842:            }
2843:
2844:            public void drawChars(char data[], int offset, int length, int x,
2845:                    int y) {
2846:
2847:                if (data == null) {
2848:                    throw new NullPointerException("char data is null");
2849:                }
2850:                if (offset < 0 || length < 0 || offset + length > data.length) {
2851:                    throw new ArrayIndexOutOfBoundsException(
2852:                            "bad offset/length");
2853:                }
2854:                if (font.hasLayoutAttributes()) {
2855:                    new TextLayout(new String(data, offset, length), font,
2856:                            getFontRenderContext()).draw(this , x, y);
2857:                    return;
2858:                }
2859:
2860:                try {
2861:                    textpipe.drawChars(this , data, offset, length, x, y);
2862:                } catch (InvalidPipeException e) {
2863:                    revalidateAll();
2864:                    try {
2865:                        textpipe.drawChars(this , data, offset, length, x, y);
2866:                    } catch (InvalidPipeException e2) {
2867:                        // Still catching the exception; we are not yet ready to
2868:                        // validate the surfaceData correctly.	Fail for now and
2869:                        // try again next time around.
2870:                    }
2871:                } finally {
2872:                    surfaceData.markDirty();
2873:                }
2874:            }
2875:
2876:            public void drawBytes(byte data[], int offset, int length, int x,
2877:                    int y) {
2878:                if (data == null) {
2879:                    throw new NullPointerException("byte data is null");
2880:                }
2881:                if (offset < 0 || length < 0 || offset + length > data.length) {
2882:                    throw new ArrayIndexOutOfBoundsException(
2883:                            "bad offset/length");
2884:                }
2885:                /* Byte data is interpreted as 8-bit ASCII. Re-use drawChars loops */
2886:                char chData[] = new char[length];
2887:                for (int i = length; i-- > 0;) {
2888:                    chData[i] = (char) (data[i + offset] & 0xff);
2889:                }
2890:                if (font.hasLayoutAttributes()) {
2891:                    new TextLayout(new String(chData), font,
2892:                            getFontRenderContext()).draw(this , x, y);
2893:                    return;
2894:                }
2895:
2896:                try {
2897:                    textpipe.drawChars(this , chData, 0, length, x, y);
2898:                } catch (InvalidPipeException e) {
2899:                    revalidateAll();
2900:                    try {
2901:                        textpipe.drawChars(this , chData, 0, length, x, y);
2902:                    } catch (InvalidPipeException e2) {
2903:                        // Still catching the exception; we are not yet ready to
2904:                        // validate the surfaceData correctly.	Fail for now and
2905:                        // try again next time around.
2906:                    }
2907:                } finally {
2908:                    surfaceData.markDirty();
2909:                }
2910:            }
2911:
2912:            // end of text rendering methods
2913:
2914:            /**
2915:             * Draws an image scaled to x,y,w,h in nonblocking mode with a
2916:             * callback object.
2917:             */
2918:            public boolean drawImage(Image img, int x, int y, int width,
2919:                    int height, ImageObserver observer) {
2920:                return drawImage(img, x, y, width, height, null, observer);
2921:            }
2922:
2923:            /**
2924:             * Not part of the advertised API but a useful utility method
2925:             * to call internally.  This is for the case where we are
2926:             * drawing to/from given coordinates using a given width/height,
2927:             * but we guarantee that the weidth/height of the src and dest
2928:             * areas are equal (no scale needed).
2929:             */
2930:            public boolean copyImage(Image img, int dx, int dy, int sx, int sy,
2931:                    int width, int height, Color bgcolor, ImageObserver observer) {
2932:                try {
2933:                    return imagepipe.copyImage(this , img, dx, dy, sx, sy,
2934:                            width, height, bgcolor, observer);
2935:                } catch (InvalidPipeException e) {
2936:                    revalidateAll();
2937:                    try {
2938:                        return imagepipe.copyImage(this , img, dx, dy, sx, sy,
2939:                                width, height, bgcolor, observer);
2940:                    } catch (InvalidPipeException e2) {
2941:                        // Still catching the exception; we are not yet ready to
2942:                        // validate the surfaceData correctly.  Fail for now and 
2943:                        // try again next time around.
2944:                        return false;
2945:                    }
2946:                } finally {
2947:                    surfaceData.markDirty();
2948:                }
2949:            }
2950:
2951:            /**
2952:             * Draws an image scaled to x,y,w,h in nonblocking mode with a
2953:             * solid background color and a callback object.
2954:             */
2955:            public boolean drawImage(Image img, int x, int y, int width,
2956:                    int height, Color bg, ImageObserver observer) {
2957:
2958:                if (img == null) {
2959:                    return true;
2960:                }
2961:
2962:                if ((width == 0) || (height == 0)) {
2963:                    return true;
2964:                }
2965:                if (width == img.getWidth(null)
2966:                        && height == img.getHeight(null)) {
2967:                    return copyImage(img, x, y, 0, 0, width, height, bg,
2968:                            observer);
2969:                }
2970:
2971:                try {
2972:                    return imagepipe.scaleImage(this , img, x, y, width, height,
2973:                            bg, observer);
2974:                } catch (InvalidPipeException e) {
2975:                    revalidateAll();
2976:                    try {
2977:                        return imagepipe.scaleImage(this , img, x, y, width,
2978:                                height, bg, observer);
2979:                    } catch (InvalidPipeException e2) {
2980:                        // Still catching the exception; we are not yet ready to
2981:                        // validate the surfaceData correctly.  Fail for now and 
2982:                        // try again next time around.
2983:                        return false;
2984:                    }
2985:                } finally {
2986:                    surfaceData.markDirty();
2987:                }
2988:            }
2989:
2990:            /**
2991:             * Draws an image at x,y in nonblocking mode.
2992:             */
2993:            public boolean drawImage(Image img, int x, int y,
2994:                    ImageObserver observer) {
2995:                return drawImage(img, x, y, null, observer);
2996:            }
2997:
2998:            /**
2999:             * Draws an image at x,y in nonblocking mode with a solid background
3000:             * color and a callback object.
3001:             */
3002:            public boolean drawImage(Image img, int x, int y, Color bg,
3003:                    ImageObserver observer) {
3004:
3005:                if (img == null) {
3006:                    return true;
3007:                }
3008:
3009:                try {
3010:                    return imagepipe.copyImage(this , img, x, y, bg, observer);
3011:                } catch (InvalidPipeException e) {
3012:                    revalidateAll();
3013:                    try {
3014:                        return imagepipe.copyImage(this , img, x, y, bg,
3015:                                observer);
3016:                    } catch (InvalidPipeException e2) {
3017:                        // Still catching the exception; we are not yet ready to
3018:                        // validate the surfaceData correctly.  Fail for now and 
3019:                        // try again next time around.
3020:                        return false;
3021:                    }
3022:                } finally {
3023:                    surfaceData.markDirty();
3024:                }
3025:            }
3026:
3027:            /**
3028:             * Draws a subrectangle of an image scaled to a destination rectangle
3029:             * in nonblocking mode with a callback object.
3030:             */
3031:            public boolean drawImage(Image img, int dx1, int dy1, int dx2,
3032:                    int dy2, int sx1, int sy1, int sx2, int sy2,
3033:                    ImageObserver observer) {
3034:                return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
3035:                        null, observer);
3036:            }
3037:
3038:            /**
3039:             * Draws a subrectangle of an image scaled to a destination rectangle in
3040:             * nonblocking mode with a solid background color and a callback object.
3041:             */
3042:            public boolean drawImage(Image img, int dx1, int dy1, int dx2,
3043:                    int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor,
3044:                    ImageObserver observer) {
3045:
3046:                if (img == null) {
3047:                    return true;
3048:                }
3049:
3050:                if (dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) {
3051:                    return true;
3052:                }
3053:
3054:                if (((sx2 - sx1) == (dx2 - dx1))
3055:                        && ((sy2 - sy1) == (dy2 - dy1))) {
3056:                    // Not a scale - forward it to a copy routine
3057:                    int srcX, srcY, dstX, dstY, width, height;
3058:                    if (sx2 > sx1) {
3059:                        width = sx2 - sx1;
3060:                        srcX = sx1;
3061:                        dstX = dx1;
3062:                    } else {
3063:                        width = sx1 - sx2;
3064:                        srcX = sx2;
3065:                        dstX = dx2;
3066:                    }
3067:                    if (sy2 > sy1) {
3068:                        height = sy2 - sy1;
3069:                        srcY = sy1;
3070:                        dstY = dy1;
3071:                    } else {
3072:                        height = sy1 - sy2;
3073:                        srcY = sy2;
3074:                        dstY = dy2;
3075:                    }
3076:                    return copyImage(img, dstX, dstY, srcX, srcY, width,
3077:                            height, bgcolor, observer);
3078:                }
3079:
3080:                try {
3081:                    return imagepipe.scaleImage(this , img, dx1, dy1, dx2, dy2,
3082:                            sx1, sy1, sx2, sy2, bgcolor, observer);
3083:                } catch (InvalidPipeException e) {
3084:                    revalidateAll();
3085:                    try {
3086:                        return imagepipe.scaleImage(this , img, dx1, dy1, dx2,
3087:                                dy2, sx1, sy1, sx2, sy2, bgcolor, observer);
3088:                    } catch (InvalidPipeException e2) {
3089:                        // Still catching the exception; we are not yet ready to
3090:                        // validate the surfaceData correctly.  Fail for now and 
3091:                        // try again next time around.
3092:                        return false;
3093:                    }
3094:                } finally {
3095:                    surfaceData.markDirty();
3096:                }
3097:            }
3098:
3099:            /**
3100:             * Draw an image, applying a transform from image space into user space
3101:             * before drawing.
3102:             * The transformation from user space into device space is done with
3103:             * the current transform in the Graphics2D.
3104:             * The given transformation is applied to the image before the
3105:             * transform attribute in the Graphics2D state is applied.
3106:             * The rendering attributes applied include the clip, transform,
3107:             * paint or color and composite attributes. Note that the result is
3108:             * undefined, if the given transform is non-invertible.
3109:             * @param img The image to be drawn.
3110:             * @param xform The transformation from image space into user space.
3111:             * @param observer The image observer to be notified on the image producing
3112:             * progress.
3113:             * @see #transform
3114:             * @see #setComposite
3115:             * @see #setClip
3116:             */
3117:            public boolean drawImage(Image img, AffineTransform xform,
3118:                    ImageObserver observer) {
3119:
3120:                if (img == null) {
3121:                    return true;
3122:                }
3123:
3124:                if (xform == null || xform.isIdentity()) {
3125:                    return drawImage(img, 0, 0, null, observer);
3126:                }
3127:
3128:                try {
3129:                    return imagepipe.transformImage(this , img, xform, observer);
3130:                } catch (InvalidPipeException e) {
3131:                    revalidateAll();
3132:                    try {
3133:                        return imagepipe.transformImage(this , img, xform,
3134:                                observer);
3135:                    } catch (InvalidPipeException e2) {
3136:                        // Still catching the exception; we are not yet ready to
3137:                        // validate the surfaceData correctly.  Fail for now and 
3138:                        // try again next time around.
3139:                        return false;
3140:                    }
3141:                } finally {
3142:                    surfaceData.markDirty();
3143:                }
3144:            }
3145:
3146:            public void drawImage(BufferedImage bImg, BufferedImageOp op,
3147:                    int x, int y) {
3148:
3149:                if (bImg == null) {
3150:                    return;
3151:                }
3152:
3153:                try {
3154:                    imagepipe.transformImage(this , bImg, op, x, y);
3155:                } catch (InvalidPipeException e) {
3156:                    revalidateAll();
3157:                    try {
3158:                        imagepipe.transformImage(this , bImg, op, x, y);
3159:                    } catch (InvalidPipeException e2) {
3160:                        // Still catching the exception; we are not yet ready to
3161:                        // validate the surfaceData correctly.  Fail for now and 
3162:                        // try again next time around.
3163:                    }
3164:                } finally {
3165:                    surfaceData.markDirty();
3166:                }
3167:            }
3168:
3169:            /**
3170:             * Get the rendering context of the font
3171:             * within this Graphics2D context.
3172:             */
3173:            public FontRenderContext getFontRenderContext() {
3174:                if (cachedFRC == null) {
3175:                    int aahint = textAntialiasHint;
3176:                    if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT
3177:                            && antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {
3178:                        aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
3179:                    }
3180:                    // Translation components should be excluded from the FRC transform
3181:                    AffineTransform tx = null;
3182:                    if (transformState >= TRANSFORM_TRANSLATESCALE) {
3183:                        if (transform.getTranslateX() == 0
3184:                                && transform.getTranslateY() == 0) {
3185:                            tx = transform;
3186:                        } else {
3187:                            tx = new AffineTransform(transform.getScaleX(),
3188:                                    transform.getShearY(), transform
3189:                                            .getShearX(),
3190:                                    transform.getScaleY(), 0, 0);
3191:                        }
3192:                    }
3193:                    cachedFRC = new FontRenderContext(tx, SunHints.Value.get(
3194:                            SunHints.INTKEY_TEXT_ANTIALIASING, aahint),
3195:                            SunHints.Value.get(
3196:                                    SunHints.INTKEY_FRACTIONALMETRICS,
3197:                                    fractionalMetricsHint));
3198:                }
3199:                return cachedFRC;
3200:            }
3201:
3202:            private FontRenderContext cachedFRC;
3203:
3204:            public void dispose() {
3205:                surfaceData = NullSurfaceData.theInstance;
3206:                invalidatePipe();
3207:            }
3208:
3209:            public void finalize() {
3210:            }
3211:
3212:            /**
3213:             * Returns destination that this Graphics renders to.  This could be
3214:             * either an Image or a Component; subclasses of SurfaceData are
3215:             * responsible for returning the appropriate object.
3216:             */
3217:            public Object getDestination() {
3218:                return surfaceData.getDestination();
3219:            }
3220:        }
ww___w_.__j__a__v___a_2__s___.___c__o__m__ | Contact Us
Copyright 2003 - 08 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.