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