public class

LoopPipe

extends Object
implements PixelDrawPipe PixelFillPipe ShapeDrawPipe
/*
 * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.java2d.pipe;

import java.awt.Font;
import java.awt.Shape;
import java.awt.BasicStroke;
import java.awt.Polygon;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.RoundRectangle2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Arc2D;
import java.awt.geom.IllegalPathStateException;
import java.awt.geom.Path2D;
import java.awt.font.GlyphVector;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.loops.FontInfo;
import sun.java2d.loops.DrawPolygons;
import sun.awt.SunHints;

public class LoopPipe
    implements PixelDrawPipe,
               PixelFillPipe,
               ShapeDrawPipe
{
    final static RenderingEngine RenderEngine = RenderingEngine.getInstance();

    public void drawLine(SunGraphics2D sg2d,
                         int x1, int y1, int x2, int y2)
    {
        int tX = sg2d.transX;
        int tY = sg2d.transY;
        sg2d.loops.drawLineLoop.DrawLine(sg2d, sg2d.getSurfaceData(),
                                         x1 + tX, y1 + tY,
                                         x2 + tX, y2 + tY);
    }

    public void drawRect(SunGraphics2D sg2d,
                         int x, int y, int width, int height)
    {
        sg2d.loops.drawRectLoop.DrawRect(sg2d, sg2d.getSurfaceData(),
                                         x + sg2d.transX,
                                         y + sg2d.transY,
                                         width, height);
    }

    public void drawRoundRect(SunGraphics2D sg2d,
                              int x, int y, int width, int height,
                              int arcWidth, int arcHeight)
    {
        sg2d.shapepipe.draw(sg2d,
                            new RoundRectangle2D.Float(x, y, width, height,
                                                       arcWidth, arcHeight));
    }

    public void drawOval(SunGraphics2D sg2d,
                         int x, int y, int width, int height)
    {
        sg2d.shapepipe.draw(sg2d, new Ellipse2D.Float(x, y, width, height));
    }

    public void drawArc(SunGraphics2D sg2d,
                        int x, int y, int width, int height,
                        int startAngle, int arcAngle)
    {
        sg2d.shapepipe.draw(sg2d, new Arc2D.Float(x, y, width, height,
                                                  startAngle, arcAngle,
                                                  Arc2D.OPEN));
    }

    public void drawPolyline(SunGraphics2D sg2d,
                             int xPoints[], int yPoints[],
                             int nPoints)
    {
        int nPointsArray[] = { nPoints };
        sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
                                                 xPoints, yPoints,
                                                 nPointsArray, 1,
                                                 sg2d.transX, sg2d.transY,
                                                 false);
    }

    public void drawPolygon(SunGraphics2D sg2d,
                            int xPoints[], int yPoints[],
                            int nPoints)
    {
        int nPointsArray[] = { nPoints };
        sg2d.loops.drawPolygonsLoop.DrawPolygons(sg2d, sg2d.getSurfaceData(),
                                                 xPoints, yPoints,
                                                 nPointsArray, 1,
                                                 sg2d.transX, sg2d.transY,
                                                 true);
    }

    public void fillRect(SunGraphics2D sg2d,
                         int x, int y, int width, int height)
    {
        sg2d.loops.fillRectLoop.FillRect(sg2d, sg2d.getSurfaceData(),
                                         x + sg2d.transX,
                                         y + sg2d.transY,
                                         width, height);
    }

    public void fillRoundRect(SunGraphics2D sg2d,
                              int x, int y, int width, int height,
                              int arcWidth, int arcHeight)
    {
        sg2d.shapepipe.fill(sg2d,
                            new RoundRectangle2D.Float(x, y, width, height,
                                                       arcWidth, arcHeight));
    }

    public void fillOval(SunGraphics2D sg2d,
                         int x, int y, int width, int height)
    {
        sg2d.shapepipe.fill(sg2d, new Ellipse2D.Float(x, y, width, height));
    }

    public void fillArc(SunGraphics2D sg2d,
                        int x, int y, int width, int height,
                        int startAngle, int arcAngle)
    {
        sg2d.shapepipe.fill(sg2d, new Arc2D.Float(x, y, width, height,
                                                  startAngle, arcAngle,
                                                  Arc2D.PIE));
    }

    public void fillPolygon(SunGraphics2D sg2d,
                            int xPoints[], int yPoints[],
                            int nPoints)
    {
        ShapeSpanIterator sr = getFillSSI(sg2d);

        try {
            sr.setOutputArea(sg2d.getCompClip());
            sr.appendPoly(xPoints, yPoints, nPoints, sg2d.transX, sg2d.transY);
            fillSpans(sg2d, sr);
        } finally {
            sr.dispose();
        }
    }


    public void draw(SunGraphics2D sg2d, Shape s) {
        if (sg2d.strokeState == sg2d.STROKE_THIN) {
            Path2D.Float p2df;
            int transX;
            int transY;
            if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
                if (s instanceof Path2D.Float) {
                    p2df = (Path2D.Float)s;
                } else {
                    p2df = new Path2D.Float(s);
                }
                transX = sg2d.transX;
                transY = sg2d.transY;
            } else {
                p2df = new Path2D.Float(s, sg2d.transform);
                transX = 0;
                transY = 0;
            }
            sg2d.loops.drawPathLoop.DrawPath(sg2d, sg2d.getSurfaceData(),
                                             transX, transY, p2df);
            return;
        }

        if (sg2d.strokeState == sg2d.STROKE_CUSTOM) {
            fill(sg2d, sg2d.stroke.createStrokedShape(s));
            return;
        }

        ShapeSpanIterator sr = getStrokeSpans(sg2d, s);

        try {
            fillSpans(sg2d, sr);
        } finally {
            sr.dispose();
        }
    }

    /**
     * Return a ShapeSpanIterator instance that normalizes as
     * appropriate for a fill operation as per the settings in
     * the specified SunGraphics2D object.
     *
     * The ShapeSpanIterator will be newly constructed and ready
     * to start taking in geometry.
     *
     * Note that the caller is responsible for calling dispose()
     * on the returned ShapeSpanIterator inside a try/finally block:
     * <pre>
     *     ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
     *     try {
     *         ssi.setOutputArea(clip);
     *         ssi.appendPath(...); // or appendPoly
     *         // iterate the spans from ssi and operate on them
     *     } finally {
     *         ssi.dispose();
     *     }
     * </pre>
     */
    public static ShapeSpanIterator getFillSSI(SunGraphics2D sg2d) {
        boolean adjust = ((sg2d.stroke instanceof BasicStroke) &&
                          sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);
        return new ShapeSpanIterator(adjust);
    }

    /*
     * Return a ShapeSpanIterator ready to iterate the spans of the wide
     * outline of Shape s using the attributes of the SunGraphics2D
     * object.
     *
     * The ShapeSpanIterator returned will be fully constructed
     * and filled with the geometry from the Shape widened by the
     * appropriate BasicStroke and normalization parameters taken
     * from the SunGraphics2D object and be ready to start returning
     * spans.
     *
     * Note that the caller is responsible for calling dispose()
     * on the returned ShapeSpanIterator inside a try/finally block.
     * <pre>
     *     ShapeSpanIterator ssi = LoopPipe.getStrokeSpans(sg2d, s);
     *     try {
     *         // iterate the spans from ssi and operate on them
     *     } finally {
     *         ssi.dispose();
     *     }
     * </pre>
     *
     * REMIND: This should return a SpanIterator interface object
     * but the caller needs to dispose() the object and that method
     * is only on ShapeSpanIterator.
     * TODO: Add a dispose() method to the SpanIterator interface.
     */
    public static ShapeSpanIterator getStrokeSpans(SunGraphics2D sg2d,
                                                   Shape s)
    {
        ShapeSpanIterator sr = new ShapeSpanIterator(false);

        try {
            sr.setOutputArea(sg2d.getCompClip());
            sr.setRule(PathIterator.WIND_NON_ZERO);

            BasicStroke bs = (BasicStroke) sg2d.stroke;
            boolean thin = (sg2d.strokeState <= sg2d.STROKE_THINDASHED);
            boolean normalize =
                (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE);

            RenderEngine.strokeTo(s,
                                  sg2d.transform, bs,
                                  thin, normalize, false, sr);
        } catch (Throwable t) {
            sr.dispose();
            sr = null;
            t.printStackTrace();
            throw new InternalError("Unable to Stroke shape ("+
                                    t.getMessage()+")");
        }
        return sr;
    }

    public void fill(SunGraphics2D sg2d, Shape s) {
        if (sg2d.strokeState == sg2d.STROKE_THIN) {
            Path2D.Float p2df;
            int transX;
            int transY;
            if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
                if (s instanceof Path2D.Float) {
                    p2df = (Path2D.Float)s;
                } else {
                    p2df = new Path2D.Float(s);
                }
                transX = sg2d.transX;
                transY = sg2d.transY;
            } else {
                p2df = new Path2D.Float(s, sg2d.transform);
                transX = 0;
                transY = 0;
            }
            sg2d.loops.fillPathLoop.FillPath(sg2d, sg2d.getSurfaceData(),
                                             transX, transY, p2df);
            return;
        }

        ShapeSpanIterator sr = getFillSSI(sg2d);
        try {
            sr.setOutputArea(sg2d.getCompClip());
            AffineTransform at =
                ((sg2d.transformState == sg2d.TRANSFORM_ISIDENT)
                 ? null
                 : sg2d.transform);
            sr.appendPath(s.getPathIterator(at));
            fillSpans(sg2d, sr);
        } finally {
            sr.dispose();
        }
    }

    private static void fillSpans(SunGraphics2D sg2d, SpanIterator si) {
        // REMIND: Eventually, the plan is that it will not be possible for
        // fs to be null since the FillSpans loop will be the fundamental
        // loop implemented for any destination type...
        if (sg2d.clipState == sg2d.CLIP_SHAPE) {
            si = sg2d.clipRegion.filter(si);
            // REMIND: Region.filter produces a Java-only iterator
            // with no native counterpart...
        } else {
            sun.java2d.loops.FillSpans fs = sg2d.loops.fillSpansLoop;
            if (fs != null) {
                fs.FillSpans(sg2d, sg2d.getSurfaceData(), si);
                return;
            }
        }
        int spanbox[] = new int[4];
        SurfaceData sd = sg2d.getSurfaceData();
        while (si.nextSpan(spanbox)) {
            int x = spanbox[0];
            int y = spanbox[1];
            int w = spanbox[2] - x;
            int h = spanbox[3] - y;
            sg2d.loops.fillRectLoop.FillRect(sg2d, sd, x, y, w, h);
        }
    }
}