/*
 * Copyright (c) 2005, 2006, 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.loops;

import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.QuadCurve2D;
import sun.awt.SunHints;
import java.util.*;

/* This is the java implementation of the native code from
 * src/share/native/sun/java2d/loops/ProcessPath.[c,h]
 * This code is written to be as much similar to the native
 * as it possible. So, it sometimes does not follow java naming conventions.
 *
 * It's important to keep this code synchronized with native one.  See more
 * comments, description and high level scheme of the rendering process in the
 * ProcessPath.c
 */

public class ProcessPath {

    /* Public interfaces and methods for drawing and filling general paths */

    public static abstract class DrawHandler {
        public int xMin;
        public int yMin;
        public int xMax;
        public int yMax;
        public float xMinf;
        public float yMinf;
        public float xMaxf;
        public float yMaxf;

        public int strokeControl;

        public DrawHandler(int xMin, int yMin, int xMax, int yMax,
                           int strokeControl)
        {
            setBounds(xMin, yMin, xMax, yMax, strokeControl);
        }

        public void setBounds(int xMin, int yMin, int xMax, int yMax)
        {
            this.xMin = xMin;
            this.yMin = yMin;
            this.xMax = xMax;
            this.yMax = yMax;

            /*                Setting up fractional clipping box
             *
             * We are using following float -> int mapping:
             *
             *      xi = floor(xf + 0.5)
             *
             * So, fractional values that hit the [xmin, xmax) integer interval
             * will be situated inside the [xmin-0.5, xmax - 0.5) fractional
             * interval. We are using EPSF constant to provide that upper
             * boundary is not included.
             */
            xMinf = xMin - 0.5f;
            yMinf = yMin - 0.5f;
            xMaxf = xMax - 0.5f - EPSF;
            yMaxf = yMax - 0.5f - EPSF;
        }

        public void setBounds(int xMin, int yMin, int xMax, int yMax,
                              int strokeControl)
        {
            this.strokeControl = strokeControl;
            setBounds(xMin, yMin, xMax, yMax);
        }

        public void adjustBounds(int bxMin, int byMin, int bxMax, int byMax)
        {
            if (xMin > bxMin) bxMin = xMin;
            if (xMax < bxMax) bxMax = xMax;
            if (yMin > byMin) byMin = yMin;
            if (yMax < byMax) byMax = yMax;
            setBounds(bxMin, byMin, bxMax, byMax);
        }

        public DrawHandler(int xMin, int yMin, int xMax, int yMax) {
            this(xMin, yMin, xMax, yMax, SunHints.INTVAL_STROKE_DEFAULT);
        }

        public abstract void drawLine(int x0, int y0, int x1, int y1);

        public abstract void drawPixel(int x0, int y0);

        public abstract void drawScanline(int x0, int x1, int y0);
    }

    public interface EndSubPathHandler {
        public void processEndSubPath();
    }

    public static final int PH_MODE_DRAW_CLIP = 0;
    public static final int PH_MODE_FILL_CLIP = 1;

    public static abstract class ProcessHandler implements EndSubPathHandler {
        DrawHandler dhnd;
        int clipMode;

        public ProcessHandler(DrawHandler dhnd,
                              int clipMode) {
            this.dhnd = dhnd;
            this.clipMode = clipMode;
        }

        public abstract void processFixedLine(int x1, int y1,
                                              int x2, int y2, int [] pixelInfo,
                                              boolean checkBounds,
                                              boolean endSubPath);
    }

    public static EndSubPathHandler noopEndSubPathHandler =
        new EndSubPathHandler() {
            public void processEndSubPath() { }
        };

    public static boolean fillPath(DrawHandler dhnd, Path2D.Float p2df,
                                   int transX, int transY)
    {
        FillProcessHandler fhnd = new FillProcessHandler(dhnd);
        if (!doProcessPath(fhnd, p2df, transX, transY)) {
            return false;
        }
        FillPolygon(fhnd, p2df.getWindingRule());
        return true;
    }

    public static boolean drawPath(DrawHandler dhnd,
                                   EndSubPathHandler endSubPath,
                                   Path2D.Float p2df,
                                   int transX, int transY)
    {
        return doProcessPath(new DrawProcessHandler(dhnd, endSubPath),
                             p2df, transX, transY);
    }

    public static boolean drawPath(DrawHandler dhnd,
                                   Path2D.Float p2df,
                                   int transX, int transY)
    {
        return doProcessPath(new DrawProcessHandler(dhnd,
                                                    noopEndSubPathHandler),
                             p2df, transX, transY);
    }

    /* Private implementation of the rendering process */

    /* Boundaries used for skipping huge path segments */
    private static final float UPPER_BND = Float.MAX_VALUE/4.0f;
    private static final float LOWER_BND = -UPPER_BND;

    /* Precision (in bits) used in forward differencing */
    private static final int FWD_PREC = 7;

    /* Precision (in bits) used for the rounding in the midpoint test */
    private static final int MDP_PREC = 10;

    private static final int MDP_MULT = 1 << MDP_PREC;
    private static final int MDP_HALF_MULT = MDP_MULT >> 1;

    /* Boundaries used for clipping large path segments (those are inside
     * [UPPER/LOWER]_BND boundaries)
     */
    private static final int UPPER_OUT_BND = 1 << (30 - MDP_PREC);
    private static final int LOWER_OUT_BND = -UPPER_OUT_BND;


    /* Calculation boundaries. They are used for switching to the more slow but
     * allowing larger input values method of calculation of the initial values
     * of the scan converted line segments inside the FillPolygon
     */
    private static final float CALC_UBND = 1 << (30 - MDP_PREC);
    private static final float CALC_LBND = -CALC_UBND;


    /* Following constants are used for providing open boundaries of the
     * intervals
     */
    public static final int EPSFX = 1;
    public static final float EPSF = ((float)EPSFX)/MDP_MULT;

    /* Bit mask used to separate whole part from the fraction part of the
     * number
     */
    private static final int MDP_W_MASK = -MDP_MULT;

    /* Bit mask used to separate fractional part from the whole part of the
     * number
     */
    private static final int MDP_F_MASK = MDP_MULT - 1;

    /*
     *                  Constants for the forward differencing
     *                      of the cubic and quad curves
     */

    /* Maximum size of the cubic curve (calculated as the size of the bounding
     * box of the control points) which could be rendered without splitting
     */
    private static final int MAX_CUB_SIZE = 256;

    /* Maximum size of the quad curve (calculated as the size of the bounding
     * box of the control points) which could be rendered without splitting
     */
    private static final int MAX_QUAD_SIZE = 1024;

    /* Default power of 2 steps used in the forward differencing. Here DF prefix
     * stands for DeFault. Constants below are used as initial values for the
     * adaptive forward differencing algorithm.
     */
    private static final int DF_CUB_STEPS = 3;
    private static final int DF_QUAD_STEPS = 2;

    /* Shift of the current point of the curve for preparing to the midpoint
     * rounding
     */
    private static final int DF_CUB_SHIFT = FWD_PREC + DF_CUB_STEPS*3 -
                                            MDP_PREC;
    private static final int DF_QUAD_SHIFT = FWD_PREC + DF_QUAD_STEPS*2 -
                                             MDP_PREC;

    /* Default amount of steps of the forward differencing */
    private static final int DF_CUB_COUNT = (1<<DF_CUB_STEPS);
    private static final int DF_QUAD_COUNT = (1<<DF_QUAD_STEPS);

    /* Default boundary constants used to check the necessity of the restepping
     */
    private static final int DF_CUB_DEC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC + 2;
    private static final int DF_CUB_INC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC - 1;
    private static final int DF_QUAD_DEC_BND = 1<<DF_QUAD_STEPS*2 +
                                                  FWD_PREC + 2;
    private static final int DF_QUAD_INC_BND = 1<<DF_QUAD_STEPS*2 +
                                                  FWD_PREC - 1;

    /* Multipliers for the coefficients of the polynomial form of the cubic and
     * quad curves representation
     */
    private static final int CUB_A_SHIFT = FWD_PREC;
    private static final int CUB_B_SHIFT = (DF_CUB_STEPS + FWD_PREC + 1);
    private static final int CUB_C_SHIFT = (DF_CUB_STEPS*2 + FWD_PREC);

    private static final int CUB_A_MDP_MULT = (1<<CUB_A_SHIFT);
    private static final int CUB_B_MDP_MULT = (1<<CUB_B_SHIFT);
    private static final int CUB_C_MDP_MULT = (1<<CUB_C_SHIFT);

    private static final int QUAD_A_SHIFT = FWD_PREC;
    private static final int QUAD_B_SHIFT = (DF_QUAD_STEPS + FWD_PREC);

    private static final int QUAD_A_MDP_MULT = (1<<QUAD_A_SHIFT);
    private static final int QUAD_B_MDP_MULT = (1<<QUAD_B_SHIFT);

    /* Clipping macros for drawing and filling algorithms */
    private static float CLIP(float a1, float b1, float a2, float b2,
                              double t) {
        return (float)(b1 + (double)(t - a1)*(b2 - b1) / (a2 - a1));
    }

    private static int CLIP(int a1, int b1, int a2, int b2, double t) {
        return (int)(b1 + (double)(t - a1)*(b2 - b1) / (a2 - a1));
    }


    private static final int CRES_MIN_CLIPPED = 0;
    private static final int CRES_MAX_CLIPPED = 1;
    private static final int CRES_NOT_CLIPPED = 3;
    private static final int CRES_INVISIBLE = 4;

    private static boolean IS_CLIPPED(int res) {
        return res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED;
    }

    /* This is java implementation of the macro from ProcessGeneralPath.c.
     * To keep the logic of the java code similar to the native one
     * array and set of indexes are used to point out the data.
     */
    private static int TESTANDCLIP(float LINE_MIN, float LINE_MAX, float[] c,
                                   int a1, int b1, int a2, int b2) {
        double t;
        int res = CRES_NOT_CLIPPED;
        if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {
            if (c[a1] < (LINE_MIN)) {
                if (c[a2] < (LINE_MIN)) {
                    return CRES_INVISIBLE;
                };
                res = CRES_MIN_CLIPPED;
                t = (LINE_MIN);
            } else {
                if (c[a2] > (LINE_MAX)) {
                    return CRES_INVISIBLE;
                };
                res = CRES_MAX_CLIPPED;
                t = (LINE_MAX);
            }
            c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);
            c[a1] = (float)t;
        }
        return res;
    }

    /* Integer version of the method above */
    private static int TESTANDCLIP(int LINE_MIN, int LINE_MAX, int[] c,
                                   int a1, int b1, int a2, int b2) {
        double t;
        int res = CRES_NOT_CLIPPED;
        if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {
            if (c[a1] < (LINE_MIN)) {
                if (c[a2] < (LINE_MIN)) {
                    return CRES_INVISIBLE;
                };
                res = CRES_MIN_CLIPPED;
                t = (LINE_MIN);
            } else {
                if (c[a2] > (LINE_MAX)) {
                    return CRES_INVISIBLE;
                };
                res = CRES_MAX_CLIPPED;
                t = (LINE_MAX);
            }
            c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);
            c[a1] = (int)t;
        }
        return res;
    }



    /* Following method is used for clipping and clumping filled shapes.
     * An example of this process is shown on the picture below:
     *                      ----+          ----+
     *                    |/    |        |/    |
     *                    +     |        +     |
     *                   /|     |        I     |
     *                  / |     |        I     |
     *                  | |     |  ===>  I     |
     *                  \ |     |        I     |
     *                   \|     |        I     |
     *                    +     |        +     |
     *                    |\    |        |\    |
     *                    | ----+        | ----+
     *                 boundary       boundary
     *
     * We can only perform clipping in case of right side of the output area
     * because all segments passed out the right boundary don't influence on the
     * result of scan conversion algorithm (it correctly handles half open
     * contours).
     *
     * This is java implementation of the macro from ProcessGeneralPath.c.
     * To keep the logic of the java code similar to the native one
     * array and set of indexes are used to point out the data.
     *
     */
    private static int CLIPCLAMP(float LINE_MIN, float LINE_MAX, float[] c,
                                 int a1, int b1, int a2, int b2,
                                 int a3, int b3) {
        c[a3] = c[a1];
        c[b3] = c[b1];
        int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);
        if (res == CRES_MIN_CLIPPED) {
            c[a3] = c[a1];
        } else if (res == CRES_MAX_CLIPPED) {
            c[a3] = c[a1];
            res = CRES_MAX_CLIPPED;
        } else if (res == CRES_INVISIBLE) {
            if (c[a1] > LINE_MAX) {
                res =  CRES_INVISIBLE;
            } else {
                c[a1] = LINE_MIN;
                c[a2] = LINE_MIN;
                res = CRES_NOT_CLIPPED;
            }
        }
        return res;
    }

    /* Integer version of the method above */
    private static int CLIPCLAMP(int LINE_MIN, int LINE_MAX, int[] c,
                                 int a1, int b1, int a2, int b2,
                                 int a3, int b3) {
        c[a3] = c[a1];
        c[b3] = c[b1];
        int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);
        if (res == CRES_MIN_CLIPPED) {
            c[a3] = c[a1];
        } else if (res == CRES_MAX_CLIPPED) {
            c[a3] = c[a1];
            res = CRES_MAX_CLIPPED;
        } else if (res == CRES_INVISIBLE) {
            if (c[a1] > LINE_MAX) {
                res =  CRES_INVISIBLE;
            } else {
                c[a1] = LINE_MIN;
                c[a2] = LINE_MIN;
                res = CRES_NOT_CLIPPED;
            }
        }
        return res;
    }

    private static class DrawProcessHandler extends ProcessHandler {

        EndSubPathHandler processESP;

        public DrawProcessHandler(DrawHandler dhnd,
                                  EndSubPathHandler processESP) {
            super(dhnd, PH_MODE_DRAW_CLIP);
            this.dhnd = dhnd;
            this.processESP = processESP;
        }

        public void processEndSubPath() {
            processESP.processEndSubPath();
        }

        void PROCESS_LINE(int fX0, int fY0, int fX1, int fY1,
                          boolean checkBounds, int[] pixelInfo) {
            int X0 = fX0 >> MDP_PREC;
            int Y0 = fY0 >> MDP_PREC;
            int X1 = fX1 >> MDP_PREC;
            int Y1 = fY1 >> MDP_PREC;

           /* Handling lines having just one pixel */
            if (((X0^X1) | (Y0^Y1)) == 0) {
                if (checkBounds &&
                    (dhnd.yMin > Y0  ||
                     dhnd.yMax <= Y0 ||
                     dhnd.xMin > X0  ||
                     dhnd.xMax <= X0)) return;

                if (pixelInfo[0] == 0) {
                    pixelInfo[0] = 1;
                    pixelInfo[1] = X0;
                    pixelInfo[2] = Y0;
                    pixelInfo[3] = X0;
                    pixelInfo[4] = Y0;
                    dhnd.drawPixel(X0, Y0);
                } else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4]) &&
                           (X0 != pixelInfo[1] || Y0 != pixelInfo[2])) {
                    dhnd.drawPixel(X0, Y0);
                    pixelInfo[3] = X0;
                    pixelInfo[4] = Y0;
                }
                return;
            }

            if (!checkBounds ||
                (dhnd.yMin <= Y0  &&
                 dhnd.yMax > Y0 &&
                 dhnd.xMin <= X0  &&
                 dhnd.xMax > X0))
            {
                /* Switch off first pixel of the line before drawing */
                if (pixelInfo[0] == 1 &&
                    ((pixelInfo[1] == X0 && pixelInfo[2] == Y0) ||
                     (pixelInfo[3] == X0 && pixelInfo[4] == Y0)))
                {
                    dhnd.drawPixel(X0, Y0);
                }
            }

            dhnd.drawLine(X0, Y0, X1, Y1);

            if (pixelInfo[0] == 0) {
                pixelInfo[0] = 1;
                pixelInfo[1] = X0;
                pixelInfo[2] = Y0;
                pixelInfo[3] = X0;
                pixelInfo[4] = Y0;
            }

            /* Switch on last pixel of the line if it was already
             * drawn during rendering of the previous segments
             */
            if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) ||
                (pixelInfo[3] == X1 && pixelInfo[4] == Y1))
            {
                if (checkBounds &&
                    (dhnd.yMin > Y1  ||
                     dhnd.yMax <= Y1 ||
                     dhnd.xMin > X1  ||
                     dhnd.xMax <= X1)) {
                    return;
                }

                dhnd.drawPixel(X1, Y1);
            }
            pixelInfo[3] = X1;
            pixelInfo[4] = Y1;
        }

        void PROCESS_POINT(int fX, int fY, boolean checkBounds,
                           int[] pixelInfo) {
            int _X = fX>> MDP_PREC;
            int _Y = fY>> MDP_PREC;
            if (checkBounds &&
                (dhnd.yMin > _Y  ||
                 dhnd.yMax <= _Y ||
                 dhnd.xMin > _X  ||
                 dhnd.xMax <= _X)) return;
            /*
             *  (_X,_Y) should be inside boundaries
             *
             *  assert(dhnd.yMin <= _Y &&
             *         dhnd.yMax >  _Y &&
             *         dhnd.xMin <= _X &&
             *         dhnd.xMax >  _X);
             *
             */
            if (pixelInfo[0] == 0) {
                pixelInfo[0] = 1;
                pixelInfo[1] = _X;
                pixelInfo[2] = _Y;
                pixelInfo[3] = _X;
                pixelInfo[4] = _Y;
                dhnd.drawPixel(_X, _Y);
            } else if ((_X != pixelInfo[3] || _Y != pixelInfo[4]) &&
                       (_X != pixelInfo[1] || _Y != pixelInfo[2])) {
                dhnd.drawPixel(_X, _Y);
                pixelInfo[3] = _X;
                pixelInfo[4] = _Y;
            }
        }

        /*                  Drawing line with subpixel endpoints
         *
         * (x1, y1), (x2, y2) -  fixed point coordinates of the endpoints
         *                       with MDP_PREC bits for the fractional part
         *
         * pixelInfo          -  structure which keeps drawing info for avoiding
         *                       multiple drawing at the same position on the
         *                       screen (required for the XOR mode of drawing)
         *
         *                          pixelInfo[0]   - state of the drawing
         *                                           0 - no pixel drawn between
         *                                           moveTo/close of the path
         *                                           1 - there are drawn pixels
         *
         *                          pixelInfo[1,2] - first pixel of the path
         *                                           between moveTo/close of the
         *                                           path
         *
         *                          pixelInfo[3,4] - last drawn pixel between
         *                                           moveTo/close of the path
         *
         * checkBounds        - flag showing necessity of checking the clip
         *
         */
        public void  processFixedLine(int x1, int y1, int x2, int y2,
                                      int[] pixelInfo, boolean checkBounds,
                                      boolean endSubPath)  {

            /* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */
            int c = ((x1 ^ x2) | (y1 ^ y2));
            int rx1, ry1, rx2, ry2;
            if ((c & MDP_W_MASK) == 0) {
                /* Checking for the segments with integer coordinates having
                 * the same start and end points
                 */
                if (c == 0) {
                    PROCESS_POINT(x1 + MDP_HALF_MULT, y1 + MDP_HALF_MULT,
                                  checkBounds, pixelInfo);
                }
                return;
            }

            if (x1 == x2 || y1 == y2) {
                rx1 = x1 + MDP_HALF_MULT;
                rx2 = x2 + MDP_HALF_MULT;
                ry1 = y1 + MDP_HALF_MULT;
                ry2 = y2 + MDP_HALF_MULT;
            } else {
                /* Neither dx nor dy can be zero because of the check above */
                int dx = x2 - x1;
                int dy = y2 - y1;

                /* Floor of x1, y1, x2, y2 */
                int fx1 = x1 & MDP_W_MASK;
                int fy1 = y1 & MDP_W_MASK;
                int fx2 = x2 & MDP_W_MASK;
                int fy2 = y2 & MDP_W_MASK;

                /* Processing first endpoint */
                if (fx1 == x1 || fy1 == y1) {
                    /* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1
                     * will not affect the result
                     */
                    rx1 = x1 + MDP_HALF_MULT;
                    ry1 = y1 + MDP_HALF_MULT;
                } else {
                    /* Boundary at the direction from (x1,y1) to (x2,y2) */
                    int bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1;
                    int by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1;

                    /* intersection with column bx1 */
                    int cross = y1 + ((bx1 - x1)*dy)/dx;
                    if (cross >= fy1 && cross <= fy1 + MDP_MULT) {
                        rx1 = bx1;
                        ry1 = cross + MDP_HALF_MULT;
                    } else {
                        /* intersection with row by1 */
                        cross = x1 + ((by1 - y1)*dx)/dy;
                        rx1 = cross + MDP_HALF_MULT;
                        ry1 = by1;
                    }
                }

                /* Processing second endpoint */
                if (fx2 == x2 || fy2 == y2) {
                    /* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2
                     * will not affect the result
                     */
                    rx2 = x2 + MDP_HALF_MULT;
                    ry2 = y2 + MDP_HALF_MULT;
                } else {
                    /* Boundary at the direction from (x2,y2) to (x1,y1) */
                    int bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2;
                    int by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2;

                    /* intersection with column bx2 */
                    int cross = y2 + ((bx2 - x2)*dy)/dx;
                    if (cross >= fy2 && cross <= fy2 + MDP_MULT) {
                        rx2 = bx2;
                        ry2 = cross + MDP_HALF_MULT;
                    } else {
                        /* intersection with row by2 */
                        cross = x2 + ((by2 - y2)*dx)/dy;
                        rx2 = cross + MDP_HALF_MULT;
                        ry2 = by2;
                    }
                }
            }
            PROCESS_LINE(rx1, ry1, rx2, ry2, checkBounds, pixelInfo);
        }
    }

    /* Performing drawing of the monotonic in X and Y quadratic curves with
     * sizes less than MAX_QUAD_SIZE by using forward differencing method of
     * calculation. See comments to the DrawMonotonicQuad in the
     * ProcessGeneralPath.c
     */
    private static void DrawMonotonicQuad(ProcessHandler hnd,
                                          float[] coords,
                                          boolean checkBounds,
                                          int[] pixelInfo) {

        int x0 = (int)(coords[0]*MDP_MULT);
        int y0 = (int)(coords[1]*MDP_MULT);

        int xe = (int)(coords[4]*MDP_MULT);
        int ye = (int)(coords[5]*MDP_MULT);

        /* Extracting fractional part of coordinates of first control point */
        int px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
        int py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;

        /* Setting default amount of steps */
        int count = DF_QUAD_COUNT;

        /* Setting default shift for preparing to the midpoint rounding */
        int shift =  DF_QUAD_SHIFT;

        int ax = (int)((coords[0] - 2*coords[2] +
                         coords[4])*QUAD_A_MDP_MULT);
        int ay = (int)((coords[1] - 2*coords[3] +
                         coords[5])*QUAD_A_MDP_MULT);

        int bx = (int)((-2*coords[0] + 2*coords[2])*QUAD_B_MDP_MULT);
        int by = (int)((-2*coords[1] + 2*coords[3])*QUAD_B_MDP_MULT);

        int ddpx = 2*ax;
        int ddpy = 2*ay;

        int dpx = ax + bx;
        int dpy = ay + by;

        int x1, y1;

        int x2 = x0;
        int y2 = y0;

        int maxDD = Math.max(Math.abs(ddpx),Math.abs(ddpy));

        int dx = xe - x0;
        int dy = ye - y0;

        int x0w = x0 & MDP_W_MASK;
        int y0w = y0 & MDP_W_MASK;

        /* Perform decreasing step in 2 times if slope of the first forward
         * difference changes too quickly (more than a pixel per step in X or Y
         * direction).  We can perform adjusting of the step size before the
         * rendering loop because the curvature of the quad curve remains the
         * same along all the curve
         */
        while (maxDD > DF_QUAD_DEC_BND) {
            dpx = (dpx<<1) - ax;
            dpy = (dpy<<1) - ay;
            count <<= 1;
            maxDD >>= 2;
            px <<=2;
            py <<=2;
            shift += 2;
        }

        while(count-- > 1) {
            px += dpx;
            py += dpy;

            dpx += ddpx;
            dpy += ddpy;

            x1 = x2;
            y1 = y2;

            x2 = x0w + (px >> shift);
            y2 = y0w + (py >> shift);

            /* Checking that we are not running out of the endpoint and bounding
             * violating coordinate.  The check is pretty simple because the
             * curve passed to the DrawCubic already splitted into the
             * monotonic in X and Y pieces
             */

            /* Bounding x2 by xe */
            if (((xe-x2)^dx) < 0) {
                x2 = xe;
            }

            /* Bounding y2 by ye */
            if (((ye-y2)^dy) < 0) {
                y2 = ye;
            }

            hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, false);
        }

        /* We are performing one step less than necessary and use actual
         * (xe,ye) endpoint of the curve instead of calculated. This prevent us
         * from running above the curve endpoint due to the accumulated errors
         * during the iterations.
         */

        hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, false);
    }

    /*
     * Checking size of the quad curves and split them if necessary.
     * Calling DrawMonotonicQuad for the curves of the appropriate size.
     * Note: coords array could be changed
     */
    private static void ProcessMonotonicQuad(ProcessHandler hnd,
                                             float[] coords,
                                             int[] pixelInfo) {

        float[] coords1 = new float[6];
        float tx, ty;
        float xMin, yMin, xMax, yMax;

        xMin = xMax = coords[0];
        yMin = yMax = coords[1];
        for (int i = 2; i < 6; i += 2) {
            xMin = (xMin > coords[i])? coords[i] : xMin;
            xMax = (xMax < coords[i])? coords[i] : xMax;
            yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin;
            yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax;
        }

        if (hnd.clipMode == PH_MODE_DRAW_CLIP) {

           /* In case of drawing we could just skip curves which are
            * completely out of bounds
            */
           if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax ||
               hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {
               return;
           }
        } else {

            /* In case of filling we could skip curves which are above,
             * below and behind the right boundary of the visible area
             */

            if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax ||
                hnd.dhnd.xMaxf < xMin)
            {
                return;
            }

            /* We could clamp x coordinates to the corresponding boundary
             * if the curve is completely behind the left one
             */

            if (hnd.dhnd.xMinf > xMax) {
                coords[0] = coords[2] = coords[4] = hnd.dhnd.xMinf;
            }
        }

        if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) {
            coords1[4] = coords[4];
            coords1[5] = coords[5];
            coords1[2] = (coords[2] + coords[4])/2.0f;
            coords1[3] = (coords[3] + coords[5])/2.0f;
            coords[2] = (coords[0] + coords[2])/2.0f;
            coords[3] = (coords[1] + coords[3])/2.0f;
            coords[4] = coords1[0] = (coords[2] + coords1[2])/2.0f;
            coords[5] = coords1[1] = (coords[3] + coords1[3])/2.0f;

            ProcessMonotonicQuad(hnd, coords, pixelInfo);

            ProcessMonotonicQuad(hnd, coords1, pixelInfo);
        } else {
            DrawMonotonicQuad(hnd, coords,
                              /* Set checkBounds parameter if curve intersects
                               * boundary of the visible area. We know that the
                               * curve is visible, so the check is pretty
                               * simple
                               */
                              hnd.dhnd.xMinf >= xMin ||
                              hnd.dhnd.xMaxf <= xMax ||
                              hnd.dhnd.yMinf >= yMin ||
                              hnd.dhnd.yMaxf <= yMax,
                              pixelInfo);
        }
    }

    /*
     * Split quadratic curve into monotonic in X and Y parts. Calling
     * ProcessMonotonicQuad for each monotonic piece of the curve.
     * Note: coords array could be changed
     */
    private static void ProcessQuad(ProcessHandler hnd, float[] coords,
                                    int[] pixelInfo) {
        /* Temporary array for holding parameters corresponding to the extreme
         * in X and Y points
         */
        double params[] = new double[2];
        int cnt = 0;
        double param;

        /* Simple check for monotonicity in X before searching for the extreme
         * points of the X(t) function. We first check if the curve is
         * monotonic in X by seeing if all of the X coordinates are strongly
         * ordered.
         */
        if ((coords[0] > coords[2] || coords[2] > coords[4]) &&
            (coords[0] < coords[2] || coords[2] < coords[4]))
        {
            /* Searching for extreme points of the X(t) function  by solving
             * dX(t)
             * ----  = 0 equation
             *  dt
             */
            double ax = coords[0] - 2*coords[2] + coords[4];
            if (ax != 0) {
                /* Calculating root of the following equation
                 * ax*t + bx = 0
                 */
                double bx = coords[0] - coords[2];

                param = bx/ax;
                if (param < 1.0 && param > 0.0) {
                    params[cnt++] = param;
                }
            }
        }

        /* Simple check for monotonicity in Y before searching for the extreme
         * points of the Y(t) function. We first check if the curve is
         * monotonic in Y by seeing if all of the Y coordinates are strongly
         * ordered.
         */
        if ((coords[1] > coords[3] || coords[3] > coords[5]) &&
            (coords[1] < coords[3] || coords[3] < coords[5]))
        {
            /* Searching for extreme points of the Y(t) function by solving
             * dY(t)
             * ----- = 0 equation
             *  dt
             */
            double ay = coords[1] - 2*coords[3] + coords[5];

            if (ay != 0) {
                /* Calculating root of the following equation
                 * ay*t + by = 0
                 */
                double by = coords[1] - coords[3];

                param = by/ay;
                if (param < 1.0 && param > 0.0) {
                    if (cnt > 0) {
                        /* Inserting parameter only if it differs from
                         * already stored
                         */
                        if (params[0] >  param) {
                            params[cnt++] = params[0];
                            params[0] = param;
                        } else if (params[0] <  param) {
                            params[cnt++] = param;
                        }
                    } else {
                        params[cnt++] = param;
                    }
                }
            }
        }

        /* Processing obtained monotonic parts */
        switch(cnt) {
            case 0:
                break;
            case 1:
                ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
                                                (float)params[0]);
                break;
            case 2:
                ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
                                                (float)params[0]);
                param = params[1] - params[0];
                if (param > 0) {
                    ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
                                           /* Scale parameter to match with
                                            * rest of the curve
                                            */
                                           (float)(param/(1.0 - params[0])));
                }
                break;
        }

        ProcessMonotonicQuad(hnd,coords,pixelInfo);
    }

    /*
     * Bite the piece of the quadratic curve from start point till the point
     * corresponding to the specified parameter then call ProcessQuad for the
     * bitten part.
     * Note: coords array will be changed
     */
    private static void ProcessFirstMonotonicPartOfQuad(ProcessHandler hnd,
                                                        float[] coords,
                                                        int[] pixelInfo,
                                                        float t) {
        float[] coords1 = new float[6];

        coords1[0] = coords[0];
        coords1[1] = coords[1];
        coords1[2] = coords[0] + t*(coords[2] - coords[0]);
        coords1[3] = coords[1] + t*(coords[3] - coords[1]);
        coords[2] = coords[2] + t*(coords[4] - coords[2]);
        coords[3] = coords[3] + t*(coords[5] - coords[3]);
        coords[0] = coords1[4] = coords1[2] + t*(coords[2] - coords1[2]);
        coords[1] = coords1[5] = coords1[3] + t*(coords[3] - coords1[3]);

        ProcessMonotonicQuad(hnd, coords1, pixelInfo);
    }

    /* Performing drawing of the monotonic in X and Y cubic curves with sizes
     * less than MAX_CUB_SIZE by using forward differencing method of
     * calculation.  See comments to the DrawMonotonicCubic in the
     * ProcessGeneralPath.c
     */
    private static void DrawMonotonicCubic(ProcessHandler hnd,
                                           float[] coords,
                                           boolean checkBounds,
                                           int[] pixelInfo) {
        int x0 = (int)(coords[0]*MDP_MULT);
        int y0 = (int)(coords[1]*MDP_MULT);

        int xe = (int)(coords[6]*MDP_MULT);
        int ye = (int)(coords[7]*MDP_MULT);

        /* Extracting fractional part of coordinates of first control point */
        int px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
        int py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;

        /* Setting default boundary values for checking first and second forward
         * difference for the necessity of the restepping. See comments to the
         * boundary values in ProcessQuad for more info.
         */
        int incStepBnd = DF_CUB_INC_BND;
        int decStepBnd = DF_CUB_DEC_BND;

        /* Setting default amount of steps */
        int count = DF_CUB_COUNT;

        /* Setting default shift for preparing to the midpoint rounding */
        int shift =  DF_CUB_SHIFT;

        int ax = (int)((-coords[0] + 3*coords[2] - 3*coords[4] +
                 coords[6])*CUB_A_MDP_MULT);
        int ay = (int)((-coords[1] + 3*coords[3] - 3*coords[5] +
                 coords[7])*CUB_A_MDP_MULT);

        int bx = (int)((3*coords[0] - 6*coords[2] +
                 3*coords[4])*CUB_B_MDP_MULT);
        int by = (int)((3*coords[1] - 6*coords[3] +
                 3*coords[5])*CUB_B_MDP_MULT);

        int cx = (int)((-3*coords[0] + 3*coords[2])*(CUB_C_MDP_MULT));
        int cy = (int)((-3*coords[1] + 3*coords[3])*(CUB_C_MDP_MULT));

        int dddpx = 6*ax;
        int dddpy = 6*ay;

        int ddpx = dddpx + bx;
        int ddpy = dddpy + by;

        int dpx = ax + (bx>>1) + cx;
        int dpy = ay + (by>>1) + cy;

        int x1, y1;

        int x2 = x0;
        int y2 = y0;

        /* Calculating whole part of the first point of the curve */
        int x0w = x0 & MDP_W_MASK;
        int y0w = y0 & MDP_W_MASK;

        int dx = xe - x0;
        int dy = ye - y0;

        while (count > 0) {
            /* Perform decreasing step in 2 times if necessary */
            while (Math.abs(ddpx) > decStepBnd ||
                   Math.abs(ddpy) > decStepBnd) {
                ddpx = (ddpx<<1) - dddpx;
                ddpy = (ddpy<<1) - dddpy;
                dpx = (dpx<<2) - (ddpx>>1);
                dpy = (dpy<<2) - (ddpy>>1);
                count <<=1;
                decStepBnd <<=3;
                incStepBnd <<=3;
                px <<=3;
                py <<=3;
                shift += 3;
            }

            /* Perform increasing step in 2 times if necessary.
             * Note: we could do it only in even steps
             */

            while ((count & 1) == 0 && shift > DF_CUB_SHIFT &&
                   Math.abs(dpx) <= incStepBnd &&
                   Math.abs(dpy) <= incStepBnd) {
                dpx = (dpx>>2) + (ddpx>>3);
                dpy = (dpy>>2) + (ddpy>>3);
                ddpx = (ddpx + dddpx)>>1;
                ddpy = (ddpy + dddpy)>>1;
                count >>=1;
                decStepBnd >>=3;
                incStepBnd >>=3;
                px >>=3;
                py >>=3;
                shift -= 3;
            }

            count--;

            /* Performing one step less than necessary and use actual (xe,ye)
             * curve's endpoint instead of calculated. This prevent us from
             * running above the curve endpoint due to the accumulated errors
             * during the iterations.
             */
            if (count > 0) {
                px += dpx;
                py += dpy;

                dpx += ddpx;
                dpy += ddpy;
                ddpx += dddpx;
                ddpy += dddpy;

                x1 = x2;
                y1 = y2;

                x2 = x0w + (px >> shift);
                y2 = y0w + (py >> shift);

                /* Checking that we are not running out of the endpoint and
                 * bounding violating coordinate.  The check is pretty simple
                 * because the curve passed to the DrawCubic already splitted
                 * into the monotonic in X and Y pieces
                 */

                /* Bounding x2 by xe */
                if (((xe-x2)^dx) < 0) {
                    x2 = xe;
                }

                /* Bounding y2 by ye */
                if (((ye-y2)^dy) < 0) {
                    y2 = ye;
                }

                hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds,
                                     false);
            } else {
                hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds,
                                     false);
            }
        }
    }

    /*
     * Checking size of the cubic curves and split them if necessary.
     * Calling DrawMonotonicCubic for the curves of the appropriate size.
     * Note: coords array could be changed
     */
    private static void ProcessMonotonicCubic(ProcessHandler hnd,
                                              float[] coords,
                                              int[] pixelInfo) {

        float[] coords1 = new float[8];
        float tx, ty;
        float xMin, xMax;
        float yMin, yMax;

        xMin = xMax = coords[0];
        yMin = yMax = coords[1];

        for (int i = 2; i < 8; i += 2) {
            xMin = (xMin > coords[i])? coords[i] : xMin;
            xMax = (xMax < coords[i])? coords[i] : xMax;
            yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin;
            yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax;
        }

        if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
            /* In case of drawing we could just skip curves which are
             * completely out of bounds
             */
            if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax ||
                hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {
                return;
            }
        } else {

            /* In case of filling we could skip curves which are above,
             * below and behind the right boundary of the visible area
             */

            if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax ||
                hnd.dhnd.xMaxf < xMin)
            {
                return;
            }

            /* We could clamp x coordinates to the corresponding boundary
             * if the curve is completely behind the left one
             */

            if (hnd.dhnd.xMinf > xMax) {
                coords[0] = coords[2] = coords[4] = coords[6] =
                    hnd.dhnd.xMinf;
            }
        }

        if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) {
            coords1[6] = coords[6];
            coords1[7] = coords[7];
            coords1[4] = (coords[4] + coords[6])/2.0f;
            coords1[5] = (coords[5] + coords[7])/2.0f;
            tx = (coords[2] + coords[4])/2.0f;
            ty = (coords[3] + coords[5])/2.0f;
            coords1[2] = (tx + coords1[4])/2.0f;
            coords1[3] = (ty + coords1[5])/2.0f;
            coords[2] =  (coords[0] + coords[2])/2.0f;
            coords[3] =  (coords[1] + coords[3])/2.0f;
            coords[4] = (coords[2] + tx)/2.0f;
            coords[5] = (coords[3] + ty)/2.0f;
            coords[6]=coords1[0]=(coords[4] + coords1[2])/2.0f;
            coords[7]=coords1[1]=(coords[5] + coords1[3])/2.0f;

            ProcessMonotonicCubic(hnd, coords, pixelInfo);

            ProcessMonotonicCubic(hnd, coords1, pixelInfo);
        } else {
            DrawMonotonicCubic(hnd, coords,
                               /* Set checkBounds parameter if curve intersects
                                * boundary of the visible area. We know that
                                * the curve is visible, so the check is pretty
                                * simple
                                */
                                hnd.dhnd.xMinf > xMin ||
                                hnd.dhnd.xMaxf < xMax ||
                                hnd.dhnd.yMinf > yMin ||
                                hnd.dhnd.yMaxf < yMax,
                                pixelInfo);
        }
    }

    /*
     * Split cubic curve into monotonic in X and Y parts. Calling
     * ProcessMonotonicCubic for each monotonic piece of the curve.
     *
     * Note: coords array could be changed
     */
    private static void ProcessCubic(ProcessHandler hnd,
                                     float[] coords,
                                     int[] pixelInfo) {
        /* Temporary array for holding parameters corresponding to the extreme
         * in X and Y points
         */
        double params[] = new double[4];
        double eqn[] = new double[3];
        double res[] = new double[2];
        int cnt = 0;

        /* Simple check for monotonicity in X before searching for the extreme
         * points of the X(t) function. We first check if the curve is
         * monotonic in X by seeing if all of the X coordinates are strongly
         * ordered.
         */
        if ((coords[0] > coords[2] || coords[2] > coords[4] ||
             coords[4] > coords[6]) &&
            (coords[0] < coords[2] || coords[2] < coords[4] ||
             coords[4] < coords[6]))
        {
            /* Searching for extreme points of the X(t) function  by solving
             * dX(t)
             * ----  = 0 equation
             *  dt
             */
            eqn[2] = -coords[0] + 3*coords[2] - 3*coords[4] + coords[6];
            eqn[1] = 2*(coords[0] - 2*coords[2] + coords[4]);
            eqn[0] = -coords[0] + coords[2];

            int nr = QuadCurve2D.solveQuadratic(eqn, res);

            /* Following code also correctly works in degenerate case of
             * the quadratic equation (nr = -1) because we do not need
             * splitting in this case.
             */
            for (int i = 0; i < nr; i++) {
                if (res[i] > 0 && res[i] < 1) {
                    params[cnt++] = res[i];
                }
            }
        }

        /* Simple check for monotonicity in Y before searching for the extreme
         * points of the Y(t) function. We first check if the curve is
         * monotonic in Y by seeing if all of the Y coordinates are strongly
         * ordered.
         */
        if ((coords[1] > coords[3] || coords[3] > coords[5] ||
             coords[5] > coords[7]) &&
            (coords[1] < coords[3] || coords[3] < coords[5] ||
             coords[5] < coords[7]))
        {
            /* Searching for extreme points of the Y(t) function by solving
             * dY(t)
             * ----- = 0 equation
             *  dt
             */
            eqn[2] = -coords[1] + 3*coords[3] - 3*coords[5] + coords[7];
            eqn[1] = 2*(coords[1] - 2*coords[3] + coords[5]);
            eqn[0] = -coords[1] + coords[3];

            int nr = QuadCurve2D.solveQuadratic(eqn, res);

            /* Following code also correctly works in degenerate case of
             * the quadratic equation (nr = -1) because we do not need
             * splitting in this case.
             */
            for (int i = 0; i < nr; i++) {
                if (res[i] > 0 && res[i] < 1) {
                    params[cnt++] = res[i];
                }
            }
        }

        if (cnt > 0) {
            /* Sorting parameter values corresponding to the extreme points
             * of the curve
             */
            Arrays.sort(params, 0, cnt);

            /* Processing obtained monotonic parts */
            ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
                                             (float)params[0]);
            for (int i = 1; i < cnt; i++) {
                double param = params[i] - params[i-1];
                if (param > 0) {
                    ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
                        /* Scale parameter to match with rest of the curve */
                        (float)(param/(1.0 - params[i - 1])));
                }
            }
        }

        ProcessMonotonicCubic(hnd,coords,pixelInfo);
    }

    /*
     * Bite the piece of the cubic curve from start point till the point
     * corresponding to the specified parameter then call ProcessCubic for the
     * bitten part.
     * Note: coords array will be changed
     */
    private static void ProcessFirstMonotonicPartOfCubic(ProcessHandler hnd,
                                                         float[] coords,
                                                         int[] pixelInfo,
                                                         float t)
    {
        float[] coords1 = new float[8];
        float tx, ty;

        coords1[0] = coords[0];
        coords1[1] = coords[1];
        tx = coords[2] + t*(coords[4] - coords[2]);
        ty = coords[3] + t*(coords[5] - coords[3]);
        coords1[2] =  coords[0] + t*(coords[2] - coords[0]);
        coords1[3] =  coords[1] + t*(coords[3] - coords[1]);
        coords1[4] = coords1[2] + t*(tx - coords1[2]);
        coords1[5] = coords1[3] + t*(ty - coords1[3]);
        coords[4] = coords[4] + t*(coords[6] - coords[4]);
        coords[5] = coords[5] + t*(coords[7] - coords[5]);
        coords[2] = tx + t*(coords[4] - tx);
        coords[3] = ty + t*(coords[5] - ty);
        coords[0]=coords1[6]=coords1[4] + t*(coords[2] - coords1[4]);
        coords[1]=coords1[7]=coords1[5] + t*(coords[3] - coords1[5]);

        ProcessMonotonicCubic(hnd, coords1, pixelInfo);
    }

    /* Note:
     * For more easy reading of the code below each java version of the macros
     * from the ProcessPath.c preceded by the commented origin call
     * containing verbose names of the parameters
     */
    private static void ProcessLine(ProcessHandler hnd, float x1, float y1,
                                    float x2, float y2, int[] pixelInfo) {
        float xMin, yMin, xMax, yMax;
        int X1, Y1, X2, Y2, X3, Y3, res;
        boolean clipped = false;
        float x3,y3;
        float c[] = new float[]{x1, y1, x2, y2, 0, 0};

        boolean lastClipped;

        xMin = hnd.dhnd.xMinf;
        yMin = hnd.dhnd.yMinf;
        xMax = hnd.dhnd.xMaxf;
        yMax = hnd.dhnd.yMaxf;

        //
        // TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, res);
        //
        res = TESTANDCLIP(yMin, yMax, c, 1, 0, 3, 2);
        if (res == CRES_INVISIBLE) return;
        clipped = IS_CLIPPED(res);
        //
        // TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, res);
        //
        res = TESTANDCLIP(yMin, yMax, c, 3, 2, 1, 0);
        if (res == CRES_INVISIBLE) return;
        lastClipped = IS_CLIPPED(res);
        clipped = clipped || lastClipped;

        if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
            //
            // TESTANDCLIP(xMin, xMax, x1, y1, x2, y2, res);
            //
            res = TESTANDCLIP(xMin, xMax, c, 0, 1, 2, 3);
            if (res == CRES_INVISIBLE) return;
            clipped = clipped || IS_CLIPPED(res);
            //
            // TESTANDCLIP(xMin, xMax, x2, y2, x1, y1, res);
            //
            res = TESTANDCLIP(xMin, xMax, c, 2, 3, 0, 1);
            if (res == CRES_INVISIBLE) return;
            lastClipped = lastClipped || IS_CLIPPED(res);
            clipped = clipped || lastClipped;
            X1 = (int)(c[0]*MDP_MULT);
            Y1 = (int)(c[1]*MDP_MULT);
            X2 = (int)(c[2]*MDP_MULT);
            Y2 = (int)(c[3]*MDP_MULT);

            hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo,
                                 clipped, /* enable boundary checking in
                                             case of clipping to avoid
                                             entering out of bounds which
                                             could happens during rounding
                                           */
                                 lastClipped /* Notify pProcessFixedLine
                                                that
                                                this is the end of the
                                                subpath (because of exiting
                                                out of boundaries)
                                              */
                                 );
        } else {
            /* Clamping starting from first vertex of the the processed
             * segment
             *
             * CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, res);
             */
            res = CLIPCLAMP(xMin, xMax, c, 0, 1, 2, 3, 4, 5);
            X1 = (int)(c[0]*MDP_MULT);
            Y1 = (int)(c[1]*MDP_MULT);

            /* Clamping only by left boundary */
            if (res == CRES_MIN_CLIPPED) {
                X3 = (int)(c[4]*MDP_MULT);
                Y3 = (int)(c[5]*MDP_MULT);
                hnd.processFixedLine(X3, Y3, X1, Y1, pixelInfo,
                                     false, lastClipped);

            } else if (res == CRES_INVISIBLE) {
                return;
            }

            /* Clamping starting from last vertex of the the processed
             * segment
             *
             * CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, res);
             */
            res = CLIPCLAMP(xMin, xMax, c, 2, 3, 0, 1, 4, 5);

            /* Checking if there was a clip by right boundary */
            lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);

            X2 = (int)(c[2]*MDP_MULT);
            Y2 = (int)(c[3]*MDP_MULT);
            hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo,
                                 false, lastClipped);

            /* Clamping only by left boundary */
            if (res == CRES_MIN_CLIPPED) {
                X3 = (int)(c[4]*MDP_MULT);
                Y3 = (int)(c[5]*MDP_MULT);
                hnd.processFixedLine(X2, Y2, X3, Y3, pixelInfo,
                                     false, lastClipped);
            }
        }
    }

    private static boolean doProcessPath(ProcessHandler hnd,
                                         Path2D.Float p2df,
                                         float transXf, float transYf) {
        float coords[] = new float[8];
        float tCoords[] = new float[8];
        float closeCoord[] = new float[] {0.0f, 0.0f};
        float firstCoord[] = new float[2];
        int pixelInfo[] = new int[5];
        boolean subpathStarted = false;
        boolean skip = false;
        float lastX, lastY;
        pixelInfo[0] = 0;

        /* Adjusting boundaries to the capabilities of the
         * ProcessPath code
         */
        hnd.dhnd.adjustBounds(LOWER_OUT_BND, LOWER_OUT_BND,
                              UPPER_OUT_BND, UPPER_OUT_BND);

        /* Adding support of the KEY_STROKE_CONTROL rendering hint.
         * Now we are supporting two modes: "pixels at centers" and
         * "pixels at corners".
         * First one is disabled by default but could be enabled by setting
         * VALUE_STROKE_PURE to the rendering hint. It means that pixel at the
         * screen (x,y) has (x + 0.5, y + 0.5) float coordinates.
         *
         * Second one is enabled by default and means straightforward mapping
         * (x,y) --> (x,y)
         */
        if (hnd.dhnd.strokeControl == SunHints.INTVAL_STROKE_PURE) {
            closeCoord[0] = -0.5f;
            closeCoord[1] = -0.5f;
            transXf -= 0.5;
            transYf -= 0.5;
        }

        PathIterator pi = p2df.getPathIterator(null);

        while (!pi.isDone()) {
            switch (pi.currentSegment(coords)) {
                case PathIterator.SEG_MOVETO:
                    /* Performing closing of the unclosed segments */
                    if (subpathStarted && !skip) {
                        if (hnd.clipMode == PH_MODE_FILL_CLIP) {
                            if (tCoords[0] != closeCoord[0] ||
                                tCoords[1] != closeCoord[1])
                            {
                                ProcessLine(hnd, tCoords[0], tCoords[1],
                                            closeCoord[0], closeCoord[1],
                                            pixelInfo);
                            }
                        }
                        hnd.processEndSubPath();
                    }

                    tCoords[0] = coords[0] + transXf;
                    tCoords[1] = coords[1] + transYf;

                    /* Checking SEG_MOVETO coordinates if they are out of the
                     * [LOWER_BND, UPPER_BND] range.  This check also handles
                     * NaN and Infinity values. Skipping next path segment in
                     * case of invalid data.
                     */

                    if (tCoords[0] < UPPER_BND &&
                        tCoords[0] > LOWER_BND &&
                        tCoords[1] < UPPER_BND &&
                        tCoords[1] > LOWER_BND)
                    {
                        subpathStarted = true;
                        skip = false;
                        closeCoord[0] = tCoords[0];
                        closeCoord[1] = tCoords[1];
                    } else {
                        skip = true;
                    }
                    pixelInfo[0] = 0;
                    break;
                case PathIterator.SEG_LINETO:
                    lastX = tCoords[2] = coords[0] + transXf;
                    lastY = tCoords[3] = coords[1] + transYf;

                    /* Checking SEG_LINETO coordinates if they are out of the
                     * [LOWER_BND, UPPER_BND] range.  This check also handles
                     * NaN and Infinity values. Ignoring current path segment
                     * in case  of invalid data. If segment is skipped its
                     * endpoint (if valid) is used to begin new subpath.
                     */

                    if (lastX < UPPER_BND &&
                        lastX > LOWER_BND &&
                        lastY < UPPER_BND &&
                        lastY > LOWER_BND)
                    {
                        if (skip) {
                            tCoords[0] = closeCoord[0] = lastX;
                            tCoords[1] = closeCoord[1] = lastY;
                            subpathStarted = true;
                            skip = false;
                        } else {
                            ProcessLine(hnd, tCoords[0], tCoords[1],
                                        tCoords[2], tCoords[3], pixelInfo);
                            tCoords[0] = lastX;
                            tCoords[1] = lastY;
                        }
                    }
                    break;
                case PathIterator.SEG_QUADTO:
                    tCoords[2] = coords[0] + transXf;
                    tCoords[3] = coords[1] + transYf;
                    lastX = tCoords[4] = coords[2] + transXf;
                    lastY = tCoords[5] = coords[3] + transYf;

                    /* Checking SEG_QUADTO coordinates if they are out of the
                     * [LOWER_BND, UPPER_BND] range.  This check also handles
                     * NaN and Infinity values. Ignoring current path segment
                     * in case  of invalid endpoints's data.  Equivalent to
                     * the SEG_LINETO if endpoint coordinates are valid but
                     * there are invalid data among other coordinates
                     */

                    if (lastX < UPPER_BND &&
                        lastX > LOWER_BND &&
                        lastY < UPPER_BND &&
                        lastY > LOWER_BND)
                    {
                        if (skip) {
                            tCoords[0] = closeCoord[0] = lastX;
                            tCoords[1] = closeCoord[1] = lastY;
                            subpathStarted = true;
                            skip = false;
                        } else {
                            if (tCoords[2] < UPPER_BND &&
                                tCoords[2] > LOWER_BND &&
                                tCoords[3] < UPPER_BND &&
                                tCoords[3] > LOWER_BND)
                            {
                                ProcessQuad(hnd, tCoords, pixelInfo);
                            } else {
                                ProcessLine(hnd, tCoords[0], tCoords[1],
                                            tCoords[4], tCoords[5],
                                            pixelInfo);
                            }
                            tCoords[0] = lastX;
                            tCoords[1] = lastY;
                        }
                    }
                    break;
                case PathIterator.SEG_CUBICTO:
                    tCoords[2] = coords[0] + transXf;
                    tCoords[3] = coords[1] + transYf;
                    tCoords[4] = coords[2] + transXf;
                    tCoords[5] = coords[3] + transYf;
                    lastX = tCoords[6] = coords[4] + transXf;
                    lastY = tCoords[7] = coords[5] + transYf;

                    /* Checking SEG_CUBICTO coordinates if they are out of the
                     * [LOWER_BND, UPPER_BND] range.  This check also handles
                     * NaN and Infinity values. Ignoring current path segment
                     * in case  of invalid endpoints's data.  Equivalent to
                     * the SEG_LINETO if endpoint coordinates are valid but
                     * there are invalid data among other coordinates
                     */

                    if (lastX < UPPER_BND &&
                        lastX > LOWER_BND &&
                        lastY < UPPER_BND &&
                        lastY > LOWER_BND)
                    {
                        if (skip) {
                            tCoords[0] = closeCoord[0] = tCoords[6];
                            tCoords[1] = closeCoord[1] = tCoords[7];
                            subpathStarted = true;
                            skip = false;
                        } else {
                            if (tCoords[2] < UPPER_BND &&
                                tCoords[2] > LOWER_BND &&
                                tCoords[3] < UPPER_BND &&
                                tCoords[3] > LOWER_BND &&
                                tCoords[4] < UPPER_BND &&
                                tCoords[4] > LOWER_BND &&
                                tCoords[5] < UPPER_BND &&
                                tCoords[5] > LOWER_BND)
                            {
                                ProcessCubic(hnd, tCoords, pixelInfo);
                            } else {
                                ProcessLine(hnd, tCoords[0], tCoords[1],
                                            tCoords[6], tCoords[7],
                                            pixelInfo);
                            }
                            tCoords[0] = lastX;
                            tCoords[1] = lastY;
                        }
                    }
                    break;
                case PathIterator.SEG_CLOSE:
                    if (subpathStarted && !skip) {
                        skip = false;
                        if (tCoords[0] != closeCoord[0] ||
                            tCoords[1] != closeCoord[1])
                        {
                            ProcessLine(hnd, tCoords[0], tCoords[1],
                                        closeCoord[0], closeCoord[1],
                                        pixelInfo);

                            /* Storing last path's point for using in following
                             * segments without initial moveTo
                             */
                            tCoords[0] = closeCoord[0];
                            tCoords[1] = closeCoord[1];
                        }
                        hnd.processEndSubPath();
                    }
                    break;
            }
            pi.next();
        }

        /* Performing closing of the unclosed segments */
        if (subpathStarted & !skip) {
            if (hnd.clipMode == PH_MODE_FILL_CLIP) {
                if (tCoords[0] != closeCoord[0] ||
                    tCoords[1] != closeCoord[1])
                {
                    ProcessLine(hnd, tCoords[0], tCoords[1],
                                closeCoord[0], closeCoord[1],
                                pixelInfo);
                }
            }
            hnd.processEndSubPath();
        }
        return true;
    }

    private static class Point {
        public int x;
        public int y;
        public boolean lastPoint;
        public Point prev;
        public Point next;
        public Point nextByY;
        public Edge edge;
        public Point(int x, int y, boolean lastPoint) {
            this.x = x;
            this.y = y;
            this.lastPoint = lastPoint;
        }
    };

    private static class Edge {
        int x;
        int dx;
        Point p;
        int  dir;
        Edge prev;
        Edge next;

        public Edge(Point p, int x, int dx, int dir) {
            this.p = p;
            this.x = x;
            this.dx = dx;
            this.dir = dir;
        }
    };

    /* Size of the default buffer in the FillData structure. This buffer is
     * replaced with heap allocated in case of large paths.
     */
    private static final int DF_MAX_POINT = 256;

    /* Following class accumulates points of the non-continuous flattened
     * general path during iteration through the origin path's segments . The
     * end of the each subpath is marked as lastPoint flag set at the last
     * point
     */
    private static class FillData {
        List<Point>  plgPnts;
        public int  plgYMin;
        public int  plgYMax;

        public FillData() {
            plgPnts = new Vector<Point>(DF_MAX_POINT);
        }

        public void addPoint(int x, int y, boolean lastPoint) {
            if (plgPnts.size() == 0) {
                plgYMin = plgYMax = y;
            } else {
                plgYMin = (plgYMin > y)?y:plgYMin;
                plgYMax = (plgYMax < y)?y:plgYMax;
            }

            plgPnts.add(new Point(x, y, lastPoint));
        }

        public boolean isEmpty() {
            return plgPnts.size() == 0;
        }

        public boolean isEnded() {
            return plgPnts.get(plgPnts.size() - 1).lastPoint;
        }

        public boolean setEnded() {
            return plgPnts.get(plgPnts.size() - 1).lastPoint = true;
        }
    }

    private static class ActiveEdgeList {
        Edge head;

        public boolean isEmpty() {
            return (head == null);
        }

        public void insert(Point pnt, int cy) {
            Point np = pnt.next;
            int X1 = pnt.x, Y1 = pnt.y;
            int X2 = np.x, Y2 = np.y;
            Edge ne;
            if (Y1 == Y2) {
                /* Skipping horizontal segments */
                return;
            } else {
                int dX = X2 - X1;
                int dY = Y2 - Y1;
                int stepx, x0, dy, dir;

                if (Y1 < Y2) {
                    x0 = X1;
                    dy = cy - Y1;
                    dir = -1;
                } else { // (Y1 > Y2)
                    x0 = X2;
                    dy = cy - Y2;
                    dir = 1;
                }

                /* We need to worry only about dX because dY is in denominator
                 * and abs(dy) < MDP_MULT (cy is a first scanline of the scan
                 * converted segment and we subtract y coordinate of the
                 * nearest segment's end from it to obtain dy)
                 */
                if (dX > CALC_UBND || dX < CALC_LBND)  {
                    stepx = (int)((((double)dX)*MDP_MULT)/dY);
                    x0 = x0 + (int)((((double)dX)*dy)/dY);
                } else {
                    stepx = (dX<<MDP_PREC)/dY;
                    x0 += (dX*dy)/dY;
                }

                ne = new Edge(pnt, x0, stepx, dir);
            }

            ne.next = head;
            ne.prev = null;
            if (head != null) {
                head.prev = ne;
            }
            head = pnt.edge = ne;
        }

        public void delete(Edge e) {
            Edge prevp = e.prev;
            Edge nextp = e.next;
            if (prevp != null) {
                prevp.next = nextp;
            } else {
                head = nextp;
            }
            if (nextp != null) {
                nextp.prev = prevp;
            }
        }

        
Bubble sorting in the ascending order of the linked list. This implementation stops processing the list if there were no changes during the previous pass. We could not use O(N) Radix sort here because in most cases list of edges almost sorted. So, bubble sort (O(N^2)) is working much better. Note, in case of array of edges Shell sort is more efficient.
/** * Bubble sorting in the ascending order of the linked list. This * implementation stops processing the list if there were no changes * during the previous pass. * * We could not use O(N) Radix sort here because in most cases list of * edges almost sorted. So, bubble sort (O(N^2)) is working much * better. Note, in case of array of edges Shell sort is more * efficient. */
public void sort() { Edge p, q, r, s = null, temp; boolean wasSwap = true; // r precedes p and s points to the node up to which // comparisons are to be made while (s != head.next && wasSwap) { r = p = head; q = p.next; wasSwap = false; while (p != s) { if (p.x >= q.x) { wasSwap = true; if (p == head) { temp = q.next; q.next = p; p.next = temp; head = q; r = q; } else { temp = q.next; q.next = p; p.next = temp; r.next = q; r = q; } } else { r = p; p = p.next; } q = p.next; if (q == s) s = p; } } // correction of the back links in the double linked edge list p = head; q = null; while (p != null) { p.prev = q; q = p; p = p.next; } } } private static void FillPolygon(FillProcessHandler hnd, int fillRule) { int k, y, n; boolean drawing; Edge active; int rightBnd = hnd.dhnd.xMax - 1; FillData fd = hnd.fd; int yMin = fd.plgYMin; int yMax = fd.plgYMax; int hashSize = ((yMax - yMin)>>MDP_PREC) + 4; /* Because of support of the KEY_STROKE_CONTROL hint we are performing * shift of the coordinates at the higher level */ int hashOffset = ((yMin - 1) & MDP_W_MASK); /* Winding counter */ int counter; /* Calculating mask to be applied to the winding counter */ int counterMask = (fillRule == PathIterator.WIND_NON_ZERO)? -1:1; int pntOffset; List<Point> pnts = fd.plgPnts; n = pnts.size(); if (n <=1) return; Point[] yHash = new Point[hashSize]; /* Creating double linked list (prev, next links) describing path order * and hash table with points which fall between scanlines. nextByY * link is used for the points which are between same scanlines. * Scanlines are passed through the centers of the pixels. */ Point curpt = pnts.get(0); curpt.prev = null; for (int i = 0; i < n - 1; i++) { curpt = pnts.get(i); Point nextpt = pnts.get(i + 1); int curHashInd = (curpt.y - hashOffset - 1) >> MDP_PREC; curpt.nextByY = yHash[curHashInd]; yHash[curHashInd] = curpt; curpt.next = nextpt; nextpt.prev = curpt; } Point ept = pnts.get(n - 1); int curHashInd = (ept.y - hashOffset - 1) >> MDP_PREC; ept.nextByY = yHash[curHashInd]; yHash[curHashInd] = ept; ActiveEdgeList activeList = new ActiveEdgeList(); for (y=hashOffset + MDP_MULT,k = 0; y<=yMax && k < hashSize; y += MDP_MULT, k++) { for(Point pt = yHash[k];pt != null; pt=pt.nextByY) { /* pt.y should be inside hashed interval * assert(y-MDP_MULT <= pt.y && pt.y < y); */ if (pt.prev != null && !pt.prev.lastPoint) { if (pt.prev.edge != null && pt.prev.y <= y) { activeList.delete(pt.prev.edge); pt.prev.edge = null; } else if (pt.prev.y > y) { activeList.insert(pt.prev, y); } } if (!pt.lastPoint && pt.next != null) { if (pt.edge != null && pt.next.y <= y) { activeList.delete(pt.edge); pt.edge = null; } else if (pt.next.y > y) { activeList.insert(pt, y); } } } if (activeList.isEmpty()) continue; activeList.sort(); counter = 0; drawing = false; int xl, xr; xl = xr = hnd.dhnd.xMin; Edge curEdge = activeList.head; while (curEdge != null) { counter += curEdge.dir; if ((counter & counterMask) != 0 && !drawing) { xl = (curEdge.x + MDP_MULT - 1)>>MDP_PREC; drawing = true; } if ((counter & counterMask) == 0 && drawing) { xr = (curEdge.x - 1) >> MDP_PREC; if (xl <= xr) { hnd.dhnd.drawScanline(xl, xr, y >> MDP_PREC); } drawing = false; } curEdge.x += curEdge.dx; curEdge = curEdge.next; } /* Performing drawing till the right boundary (for correct * rendering shapes clipped at the right side) */ if (drawing && xl <= rightBnd) { /* Support of the strokeHint was added into the * draw and fill methods of the sun.java2d.pipe.LoopPipe */ hnd.dhnd.drawScanline(xl, rightBnd, y >> MDP_PREC); } } } private static class FillProcessHandler extends ProcessHandler { FillData fd; /* Note: For more easy reading of the code below each java version of * the macros from the ProcessPath.c preceded by the commented * origin call containing verbose names of the parameters */ public void processFixedLine(int x1, int y1, int x2, int y2, int[] pixelInfo, boolean checkBounds, boolean endSubPath) { int outXMin, outXMax, outYMin, outYMax; int res; /* There is no need to round line coordinates to the forward * differencing precision anymore. Such a rounding was used for * preventing the curve go out the endpoint (this sometimes does * not help). The problem was fixed in the forward differencing * loops. */ if (checkBounds) { boolean lastClipped; /* This function is used only for filling shapes, so there is no * check for the type of clipping */ int c[] = new int[]{x1, y1, x2, y2, 0, 0}; outXMin = (int)(dhnd.xMinf * MDP_MULT); outXMax = (int)(dhnd.xMaxf * MDP_MULT); outYMin = (int)(dhnd.yMinf * MDP_MULT); outYMax = (int)(dhnd.yMaxf * MDP_MULT); /* * TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, res); */ res = TESTANDCLIP(outYMin, outYMax, c, 1, 0, 3, 2); if (res == CRES_INVISIBLE) return; /* * TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, res); */ res = TESTANDCLIP(outYMin, outYMax, c, 3, 2, 1, 0); if (res == CRES_INVISIBLE) return; lastClipped = IS_CLIPPED(res); /* Clamping starting from first vertex of the the processed * segment * * CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, res); */ res = CLIPCLAMP(outXMin, outXMax, c, 0, 1, 2, 3, 4, 5); /* Clamping only by left boundary */ if (res == CRES_MIN_CLIPPED) { processFixedLine(c[4], c[5], c[0], c[1], pixelInfo, false, lastClipped); } else if (res == CRES_INVISIBLE) { return; } /* Clamping starting from last vertex of the the processed * segment * * CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, res); */ res = CLIPCLAMP(outXMin, outXMax, c, 2, 3, 0, 1, 4, 5); /* Checking if there was a clip by right boundary */ lastClipped = lastClipped || (res == CRES_MAX_CLIPPED); processFixedLine(c[0], c[1], c[2], c[3], pixelInfo, false, lastClipped); /* Clamping only by left boundary */ if (res == CRES_MIN_CLIPPED) { processFixedLine(c[2], c[3], c[4], c[5], pixelInfo, false, lastClipped); } return; } /* Adding first point of the line only in case of empty or just * finished path */ if (fd.isEmpty() || fd.isEnded()) { fd.addPoint(x1, y1, false); } fd.addPoint(x2, y2, false); if (endSubPath) { fd.setEnded(); } } FillProcessHandler(DrawHandler dhnd) { super(dhnd, PH_MODE_FILL_CLIP); this.fd = new FillData(); } public void processEndSubPath() { if (!fd.isEmpty()) { fd.setEnded(); } } } }