1 /*
   2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * 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.java2d.loops.FillParallelogram;
  45 import sun.java2d.loops.DrawParallelogram;
  46 import sun.awt.SunHints;
  47 
  48 public class LoopPipe
  49     implements PixelDrawPipe,
  50                PixelFillPipe,
  51                ParallelogramPipe,
  52                ShapeDrawPipe,
  53                LoopBasedPipe
  54 {
  55     final static RenderingEngine RenderEngine = RenderingEngine.getInstance();
  56 
  57     public void drawLine(SunGraphics2D sg2d,
  58                          int x1, int y1, int x2, int y2)
  59     {
  60         int tX = sg2d.transX;
  61         int tY = sg2d.transY;
  62         sg2d.loops.drawLineLoop.DrawLine(sg2d, sg2d.getSurfaceData(),
  63                                          x1 + tX, y1 + tY,
  64                                          x2 + tX, y2 + tY);
  65     }
  66 
  67     public void drawRect(SunGraphics2D sg2d,
  68                          int x, int y, int width, int height)
  69     {
  70         sg2d.loops.drawRectLoop.DrawRect(sg2d, sg2d.getSurfaceData(),
  71                                          x + sg2d.transX,
  72                                          y + sg2d.transY,
  73                                          width, height);
  74     }
  75 
  76     public void drawRoundRect(SunGraphics2D sg2d,
  77                               int x, int y, int width, int height,
  78                               int arcWidth, int arcHeight)
  79     {
  80         sg2d.shapepipe.draw(sg2d,
  81                             new RoundRectangle2D.Float(x, y, width, height,
  82                                                        arcWidth, arcHeight));
  83     }
  84 
  85     public void drawOval(SunGraphics2D sg2d,
  86                          int x, int y, int width, int height)
  87     {
  88         sg2d.shapepipe.draw(sg2d, new Ellipse2D.Float(x, y, width, height));
  89     }
  90 
  91     public void drawArc(SunGraphics2D sg2d,
  92                         int x, int y, int width, int height,
  93                         int startAngle, int arcAngle)
  94     {
  95         sg2d.shapepipe.draw(sg2d, new Arc2D.Float(x, y, width, height,
  96                                                   startAngle, arcAngle,
  97                                                   Arc2D.OPEN));
  98     }
  99 
 100     public void drawPolyline(SunGraphics2D sg2d,
 101                              int xPoints[], int yPoints[],
 102                              int nPoints)
 103     {
 104         int nPointsArray[] = { nPoints };
 105         sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
 106                                                  xPoints, yPoints,
 107                                                  nPointsArray, 1,
 108                                                  sg2d.transX, sg2d.transY,
 109                                                  false);
 110     }
 111 
 112     public void drawPolygon(SunGraphics2D sg2d,
 113                             int xPoints[], int yPoints[],
 114                             int nPoints)
 115     {
 116         int nPointsArray[] = { nPoints };
 117         sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
 118                                                  xPoints, yPoints,
 119                                                  nPointsArray, 1,
 120                                                  sg2d.transX, sg2d.transY,
 121                                                  true);
 122     }
 123 
 124     public void fillRect(SunGraphics2D sg2d,
 125                          int x, int y, int width, int height)
 126     {
 127         sg2d.loops.fillRectLoop.FillRect(sg2d, sg2d.getSurfaceData(),
 128                                          x + sg2d.transX,
 129                                          y + sg2d.transY,
 130                                          width, height);
 131     }
 132 
 133     public void fillRoundRect(SunGraphics2D sg2d,
 134                               int x, int y, int width, int height,
 135                               int arcWidth, int arcHeight)
 136     {
 137         sg2d.shapepipe.fill(sg2d,
 138                             new RoundRectangle2D.Float(x, y, width, height,
 139                                                        arcWidth, arcHeight));
 140     }
 141 
 142     public void fillOval(SunGraphics2D sg2d,
 143                          int x, int y, int width, int height)
 144     {
 145         sg2d.shapepipe.fill(sg2d, new Ellipse2D.Float(x, y, width, height));
 146     }
 147 
 148     public void fillArc(SunGraphics2D sg2d,
 149                         int x, int y, int width, int height,
 150                         int startAngle, int arcAngle)
 151     {
 152         sg2d.shapepipe.fill(sg2d, new Arc2D.Float(x, y, width, height,
 153                                                   startAngle, arcAngle,
 154                                                   Arc2D.PIE));
 155     }
 156 
 157     public void fillPolygon(SunGraphics2D sg2d,
 158                             int xPoints[], int yPoints[],
 159                             int nPoints)
 160     {
 161         ShapeSpanIterator sr = getFillSSI(sg2d);
 162 
 163         try {
 164             sr.setOutputArea(sg2d.getCompClip());
 165             sr.appendPoly(xPoints, yPoints, nPoints, sg2d.transX, sg2d.transY);
 166             fillSpans(sg2d, sr);
 167         } finally {
 168             sr.dispose();
 169         }
 170     }
 171 
 172 
 173     public void draw(SunGraphics2D sg2d, Shape s) {
 174         if (sg2d.strokeState == sg2d.STROKE_THIN) {
 175             Path2D.Float p2df;
 176             int transX;
 177             int transY;
 178             if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 179                 if (s instanceof Path2D.Float) {
 180                     p2df = (Path2D.Float)s;
 181                 } else {
 182                     p2df = new Path2D.Float(s);
 183                 }
 184                 transX = sg2d.transX;
 185                 transY = sg2d.transY;
 186             } else {
 187                 p2df = new Path2D.Float(s, sg2d.transform);
 188                 transX = 0;
 189                 transY = 0;
 190             }
 191             sg2d.loops.drawPathLoop.DrawPath(sg2d, sg2d.getSurfaceData(),
 192                                              transX, transY, p2df);
 193             return;
 194         }
 195 
 196         if (sg2d.strokeState == sg2d.STROKE_CUSTOM) {
 197             fill(sg2d, sg2d.stroke.createStrokedShape(s));
 198             return;
 199         }
 200 
 201         ShapeSpanIterator sr = getStrokeSpans(sg2d, s);
 202 
 203         try {
 204             fillSpans(sg2d, sr);
 205         } finally {
 206             sr.dispose();
 207         }
 208     }
 209 
 210     /**
 211      * Return a ShapeSpanIterator instance that normalizes as
 212      * appropriate for a fill operation as per the settings in
 213      * the specified SunGraphics2D object.
 214      *
 215      * The ShapeSpanIterator will be newly constructed and ready
 216      * to start taking in geometry.
 217      *
 218      * Note that the caller is responsible for calling dispose()
 219      * on the returned ShapeSpanIterator inside a try/finally block:
 220      * <pre>
 221      *     ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
 222      *     try {
 223      *         ssi.setOutputArea(clip);
 224      *         ssi.appendPath(...); // or appendPoly
 225      *         // iterate the spans from ssi and operate on them
 226      *     } finally {
 227      *         ssi.dispose();
 228      *     }
 229      * </pre>
 230      */
 231     public static ShapeSpanIterator getFillSSI(SunGraphics2D sg2d) {
 232         boolean adjust = ((sg2d.stroke instanceof BasicStroke) &&
 233                           sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
 234         return new ShapeSpanIterator(adjust);
 235     }
 236 
 237     /*
 238      * Return a ShapeSpanIterator ready to iterate the spans of the wide
 239      * outline of Shape s using the attributes of the SunGraphics2D
 240      * object.
 241      *
 242      * The ShapeSpanIterator returned will be fully constructed
 243      * and filled with the geometry from the Shape widened by the
 244      * appropriate BasicStroke and normalization parameters taken
 245      * from the SunGraphics2D object and be ready to start returning
 246      * spans.
 247      *
 248      * Note that the caller is responsible for calling dispose()
 249      * on the returned ShapeSpanIterator inside a try/finally block.
 250      * <pre>
 251      *     ShapeSpanIterator ssi = LoopPipe.getStrokeSpans(sg2d, s);
 252      *     try {
 253      *         // iterate the spans from ssi and operate on them
 254      *     } finally {
 255      *         ssi.dispose();
 256      *     }
 257      * </pre>
 258      *
 259      * REMIND: This should return a SpanIterator interface object
 260      * but the caller needs to dispose() the object and that method
 261      * is only on ShapeSpanIterator.
 262      * TODO: Add a dispose() method to the SpanIterator interface.
 263      */
 264     public static ShapeSpanIterator getStrokeSpans(SunGraphics2D sg2d,
 265                                                    Shape s)
 266     {
 267         ShapeSpanIterator sr = new ShapeSpanIterator(false);
 268 
 269         try {
 270             sr.setOutputArea(sg2d.getCompClip());
 271             sr.setRule(PathIterator.WIND_NON_ZERO);
 272 
 273             BasicStroke bs = (BasicStroke) sg2d.stroke;
 274             boolean thin = (sg2d.strokeState <= sg2d.STROKE_THINDASHED);
 275             boolean normalize =
 276                 (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
 277 
 278             RenderEngine.strokeTo(s,
 279                                   sg2d.transform, bs,
 280                                   thin, normalize, false, sr);
 281         } catch (Throwable t) {
 282             sr.dispose();
 283             sr = null;
 284             throw new InternalError("Unable to Stroke shape ("+
 285                                     t.getMessage()+")", t);
 286         }
 287         return sr;
 288     }
 289 
 290     public void fill(SunGraphics2D sg2d, Shape s) {
 291         if (sg2d.strokeState == sg2d.STROKE_THIN) {
 292             Path2D.Float p2df;
 293             int transX;
 294             int transY;
 295             if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 296                 if (s instanceof Path2D.Float) {
 297                     p2df = (Path2D.Float)s;
 298                 } else {
 299                     p2df = new Path2D.Float(s);
 300                 }
 301                 transX = sg2d.transX;
 302                 transY = sg2d.transY;
 303             } else {
 304                 p2df = new Path2D.Float(s, sg2d.transform);
 305                 transX = 0;
 306                 transY = 0;
 307             }
 308             sg2d.loops.fillPathLoop.FillPath(sg2d, sg2d.getSurfaceData(),
 309                                              transX, transY, p2df);
 310             return;
 311         }
 312 
 313         ShapeSpanIterator sr = getFillSSI(sg2d);
 314         try {
 315             sr.setOutputArea(sg2d.getCompClip());
 316             AffineTransform at =
 317                 ((sg2d.transformState == sg2d.TRANSFORM_ISIDENT)
 318                  ? null
 319                  : sg2d.transform);
 320             sr.appendPath(s.getPathIterator(at));
 321             fillSpans(sg2d, sr);
 322         } finally {
 323             sr.dispose();
 324         }
 325     }
 326 
 327     private static void fillSpans(SunGraphics2D sg2d, SpanIterator si) {
 328         // REMIND: Eventually, the plan is that it will not be possible for
 329         // fs to be null since the FillSpans loop will be the fundamental
 330         // loop implemented for any destination type...
 331         if (sg2d.clipState == sg2d.CLIP_SHAPE) {
 332             si = sg2d.clipRegion.filter(si);
 333             // REMIND: Region.filter produces a Java-only iterator
 334             // with no native counterpart...
 335         } else {
 336             sun.java2d.loops.FillSpans fs = sg2d.loops.fillSpansLoop;
 337             if (fs != null) {
 338                 fs.FillSpans(sg2d, sg2d.getSurfaceData(), si);
 339                 return;
 340             }
 341         }
 342         int spanbox[] = new int[4];
 343         SurfaceData sd = sg2d.getSurfaceData();
 344         while (si.nextSpan(spanbox)) {
 345             int x = spanbox[0];
 346             int y = spanbox[1];
 347             int w = spanbox[2] - x;
 348             int h = spanbox[3] - y;
 349             sg2d.loops.fillRectLoop.FillRect(sg2d, sd, x, y, w, h);
 350         }
 351     }
 352 
 353     public void fillParallelogram(SunGraphics2D sg2d,
 354                                   double ux1, double uy1,
 355                                   double ux2, double uy2,
 356                                   double x, double y,
 357                                   double dx1, double dy1,
 358                                   double dx2, double dy2)
 359     {
 360         FillParallelogram fp = sg2d.loops.fillParallelogramLoop;
 361         fp.FillParallelogram(sg2d, sg2d.getSurfaceData(),
 362                              x, y, dx1, dy1, dx2, dy2);
 363     }
 364 
 365     public void drawParallelogram(SunGraphics2D sg2d,
 366                                   double ux1, double uy1,
 367                                   double ux2, double uy2,
 368                                   double x, double y,
 369                                   double dx1, double dy1,
 370                                   double dx2, double dy2,
 371                                   double lw1, double lw2)
 372     {
 373         DrawParallelogram dp = sg2d.loops.drawParallelogramLoop;
 374         dp.DrawParallelogram(sg2d, sg2d.getSurfaceData(),
 375                              x, y, dx1, dy1, dx2, dy2, lw1, lw2);
 376     }
 377 }