1 /*
   2  * Copyright 1999-2007 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.java2d.pipe;
  27 
  28 import java.awt.Font;
  29 import java.awt.Shape;
  30 import java.awt.BasicStroke;
  31 import java.awt.Polygon;
  32 import java.awt.geom.AffineTransform;
  33 import java.awt.geom.PathIterator;
  34 import java.awt.geom.RoundRectangle2D;
  35 import java.awt.geom.Ellipse2D;
  36 import java.awt.geom.Arc2D;
  37 import java.awt.geom.IllegalPathStateException;
  38 import java.awt.geom.Path2D;
  39 import java.awt.font.GlyphVector;
  40 import sun.java2d.SunGraphics2D;
  41 import sun.java2d.SurfaceData;
  42 import sun.java2d.loops.FontInfo;
  43 import sun.java2d.loops.DrawPolygons;
  44 import sun.awt.SunHints;
  45 
  46 public class LoopPipe
  47     implements PixelDrawPipe,
  48                PixelFillPipe,
  49                ShapeDrawPipe
  50 {
  51     final static RenderingEngine RenderEngine = RenderingEngine.getInstance();
  52 
  53     public void drawLine(SunGraphics2D sg2d,
  54                          int x1, int y1, int x2, int y2)
  55     {
  56         int tX = sg2d.transX;
  57         int tY = sg2d.transY;
  58         sg2d.loops.drawLineLoop.DrawLine(sg2d, sg2d.getSurfaceData(),
  59                                          x1 + tX, y1 + tY,
  60                                          x2 + tX, y2 + tY);
  61     }
  62 
  63     public void drawRect(SunGraphics2D sg2d,
  64                          int x, int y, int width, int height)
  65     {
  66         sg2d.loops.drawRectLoop.DrawRect(sg2d, sg2d.getSurfaceData(),
  67                                          x + sg2d.transX,
  68                                          y + sg2d.transY,
  69                                          width, height);
  70     }
  71 
  72     public void drawRoundRect(SunGraphics2D sg2d,
  73                               int x, int y, int width, int height,
  74                               int arcWidth, int arcHeight)
  75     {
  76         sg2d.shapepipe.draw(sg2d,
  77                             new RoundRectangle2D.Float(x, y, width, height,
  78                                                        arcWidth, arcHeight));
  79     }
  80 
  81     public void drawOval(SunGraphics2D sg2d,
  82                          int x, int y, int width, int height)
  83     {
  84         sg2d.shapepipe.draw(sg2d, new Ellipse2D.Float(x, y, width, height));
  85     }
  86 
  87     public void drawArc(SunGraphics2D sg2d,
  88                         int x, int y, int width, int height,
  89                         int startAngle, int arcAngle)
  90     {
  91         sg2d.shapepipe.draw(sg2d, new Arc2D.Float(x, y, width, height,
  92                                                   startAngle, arcAngle,
  93                                                   Arc2D.OPEN));
  94     }
  95 
  96     public void drawPolyline(SunGraphics2D sg2d,
  97                              int xPoints[], int yPoints[],
  98                              int nPoints)
  99     {
 100         int nPointsArray[] = { nPoints };
 101         sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
 102                                                  xPoints, yPoints,
 103                                                  nPointsArray, 1,
 104                                                  sg2d.transX, sg2d.transY,
 105                                                  false);
 106     }
 107 
 108     public void drawPolygon(SunGraphics2D sg2d,
 109                             int xPoints[], int yPoints[],
 110                             int nPoints)
 111     {
 112         int nPointsArray[] = { nPoints };
 113         sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
 114                                                  xPoints, yPoints,
 115                                                  nPointsArray, 1,
 116                                                  sg2d.transX, sg2d.transY,
 117                                                  true);
 118     }
 119 
 120     public void fillRect(SunGraphics2D sg2d,
 121                          int x, int y, int width, int height)
 122     {
 123         sg2d.loops.fillRectLoop.FillRect(sg2d, sg2d.getSurfaceData(),
 124                                          x + sg2d.transX,
 125                                          y + sg2d.transY,
 126                                          width, height);
 127     }
 128 
 129     public void fillRoundRect(SunGraphics2D sg2d,
 130                               int x, int y, int width, int height,
 131                               int arcWidth, int arcHeight)
 132     {
 133         sg2d.shapepipe.fill(sg2d,
 134                             new RoundRectangle2D.Float(x, y, width, height,
 135                                                        arcWidth, arcHeight));
 136     }
 137 
 138     public void fillOval(SunGraphics2D sg2d,
 139                          int x, int y, int width, int height)
 140     {
 141         sg2d.shapepipe.fill(sg2d, new Ellipse2D.Float(x, y, width, height));
 142     }
 143 
 144     public void fillArc(SunGraphics2D sg2d,
 145                         int x, int y, int width, int height,
 146                         int startAngle, int arcAngle)
 147     {
 148         sg2d.shapepipe.fill(sg2d, new Arc2D.Float(x, y, width, height,
 149                                                   startAngle, arcAngle,
 150                                                   Arc2D.PIE));
 151     }
 152 
 153     public void fillPolygon(SunGraphics2D sg2d,
 154                             int xPoints[], int yPoints[],
 155                             int nPoints)
 156     {
 157         ShapeSpanIterator sr = getFillSSI(sg2d);
 158 
 159         try {
 160             sr.setOutputArea(sg2d.getCompClip());
 161             sr.appendPoly(xPoints, yPoints, nPoints, sg2d.transX, sg2d.transY);
 162             fillSpans(sg2d, sr);
 163         } finally {
 164             sr.dispose();
 165         }
 166     }
 167 
 168 
 169     public void draw(SunGraphics2D sg2d, Shape s) {
 170         if (sg2d.strokeState == sg2d.STROKE_THIN) {
 171             Path2D.Float p2df;
 172             int transX;
 173             int transY;
 174             if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 175                 if (s instanceof Path2D.Float) {
 176                     p2df = (Path2D.Float)s;
 177                 } else {
 178                     p2df = new Path2D.Float(s);
 179                 }
 180                 transX = sg2d.transX;
 181                 transY = sg2d.transY;
 182             } else {
 183                 p2df = new Path2D.Float(s, sg2d.transform);
 184                 transX = 0;
 185                 transY = 0;
 186             }
 187             sg2d.loops.drawPathLoop.DrawPath(sg2d, sg2d.getSurfaceData(),
 188                                              transX, transY, p2df);
 189             return;
 190         }
 191 
 192         if (sg2d.strokeState == sg2d.STROKE_CUSTOM) {
 193             fill(sg2d, sg2d.stroke.createStrokedShape(s));
 194             return;
 195         }
 196 
 197         ShapeSpanIterator sr = getStrokeSpans(sg2d, s);
 198 
 199         try {
 200             fillSpans(sg2d, sr);
 201         } finally {
 202             sr.dispose();
 203         }
 204     }
 205 
 206     /**
 207      * Return a ShapeSpanIterator instance that normalizes as
 208      * appropriate for a fill operation as per the settings in
 209      * the specified SunGraphics2D object.
 210      *
 211      * The ShapeSpanIterator will be newly constructed and ready
 212      * to start taking in geometry.
 213      *
 214      * Note that the caller is responsible for calling dispose()
 215      * on the returned ShapeSpanIterator inside a try/finally block:
 216      * <pre>
 217      *     ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
 218      *     try {
 219      *         ssi.setOutputArea(clip);
 220      *         ssi.appendPath(...); // or appendPoly
 221      *         // iterate the spans from ssi and operate on them
 222      *     } finally {
 223      *         ssi.dispose();
 224      *     }
 225      * </pre>
 226      */
 227     public static ShapeSpanIterator getFillSSI(SunGraphics2D sg2d) {
 228         boolean adjust = ((sg2d.stroke instanceof BasicStroke) &&
 229                           sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
 230         return new ShapeSpanIterator(adjust);
 231     }
 232 
 233     /*
 234      * Return a ShapeSpanIterator ready to iterate the spans of the wide
 235      * outline of Shape s using the attributes of the SunGraphics2D
 236      * object.
 237      *
 238      * The ShapeSpanIterator returned will be fully constructed
 239      * and filled with the geometry from the Shape widened by the
 240      * appropriate BasicStroke and normalization parameters taken
 241      * from the SunGraphics2D object and be ready to start returning
 242      * spans.
 243      *
 244      * Note that the caller is responsible for calling dispose()
 245      * on the returned ShapeSpanIterator inside a try/finally block.
 246      * <pre>
 247      *     ShapeSpanIterator ssi = LoopPipe.getStrokeSpans(sg2d, s);
 248      *     try {
 249      *         // iterate the spans from ssi and operate on them
 250      *     } finally {
 251      *         ssi.dispose();
 252      *     }
 253      * </pre>
 254      *
 255      * REMIND: This should return a SpanIterator interface object
 256      * but the caller needs to dispose() the object and that method
 257      * is only on ShapeSpanIterator.
 258      * TODO: Add a dispose() method to the SpanIterator interface.
 259      */
 260     public static ShapeSpanIterator getStrokeSpans(SunGraphics2D sg2d,
 261                                                    Shape s)
 262     {
 263         ShapeSpanIterator sr = new ShapeSpanIterator(false);
 264 
 265         try {
 266             sr.setOutputArea(sg2d.getCompClip());
 267             sr.setRule(PathIterator.WIND_NON_ZERO);
 268 
 269             BasicStroke bs = (BasicStroke) sg2d.stroke;
 270             boolean thin = (sg2d.strokeState <= sg2d.STROKE_THINDASHED);
 271             boolean normalize =
 272                 (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
 273 
 274             RenderEngine.strokeTo(s,
 275                                   sg2d.transform, bs,
 276                                   thin, normalize, false, sr);
 277         } catch (Throwable t) {
 278             sr.dispose();
 279             sr = null;
 280             t.printStackTrace();
 281             throw new InternalError("Unable to Stroke shape ("+
 282                                     t.getMessage()+")");
 283         }
 284         return sr;
 285     }
 286 
 287     public void fill(SunGraphics2D sg2d, Shape s) {
 288         if (sg2d.strokeState == sg2d.STROKE_THIN) {
 289             Path2D.Float p2df;
 290             int transX;
 291             int transY;
 292             if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 293                 if (s instanceof Path2D.Float) {
 294                     p2df = (Path2D.Float)s;
 295                 } else {
 296                     p2df = new Path2D.Float(s);
 297                 }
 298                 transX = sg2d.transX;
 299                 transY = sg2d.transY;
 300             } else {
 301                 p2df = new Path2D.Float(s, sg2d.transform);
 302                 transX = 0;
 303                 transY = 0;
 304             }
 305             sg2d.loops.fillPathLoop.FillPath(sg2d, sg2d.getSurfaceData(),
 306                                              transX, transY, p2df);
 307             return;
 308         }
 309 
 310         ShapeSpanIterator sr = getFillSSI(sg2d);
 311         try {
 312             sr.setOutputArea(sg2d.getCompClip());
 313             AffineTransform at =
 314                 ((sg2d.transformState == sg2d.TRANSFORM_ISIDENT)
 315                  ? null
 316                  : sg2d.transform);
 317             sr.appendPath(s.getPathIterator(at));
 318             fillSpans(sg2d, sr);
 319         } finally {
 320             sr.dispose();
 321         }
 322     }
 323 
 324     private static void fillSpans(SunGraphics2D sg2d, SpanIterator si) {
 325         // REMIND: Eventually, the plan is that it will not be possible for
 326         // fs to be null since the FillSpans loop will be the fundamental
 327         // loop implemented for any destination type...
 328         if (sg2d.clipState == sg2d.CLIP_SHAPE) {
 329             si = sg2d.clipRegion.filter(si);
 330             // REMIND: Region.filter produces a Java-only iterator
 331             // with no native counterpart...
 332         } else {
 333             sun.java2d.loops.FillSpans fs = sg2d.loops.fillSpansLoop;
 334             if (fs != null) {
 335                 fs.FillSpans(sg2d, sg2d.getSurfaceData(), si);
 336                 return;
 337             }
 338         }
 339         int spanbox[] = new int[4];
 340         SurfaceData sd = sg2d.getSurfaceData();
 341         while (si.nextSpan(spanbox)) {
 342             int x = spanbox[0];
 343             int y = spanbox[1];
 344             int w = spanbox[2] - x;
 345             int h = spanbox[3] - y;
 346             sg2d.loops.fillRectLoop.FillRect(sg2d, sd, x, y, w, h);
 347         }
 348     }
 349 }