/*

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
package org.apache.batik.ext.awt;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;

/**
 * Provides the actual implementation for the RadialGradientPaint.
 * This is where the pixel processing is done.  A RadialGradienPaint
 * only supports circular gradients, but it should be possible to scale
 * the circle to look approximately elliptical, by means of a
 * gradient transform passed into the RadialGradientPaint constructor.
 *
 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
 * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
 * @version $Id: RadialGradientPaintContext.java 1733416 2016-03-03 07:07:13Z gadams $
 *
 */
final class RadialGradientPaintContext extends MultipleGradientPaintContext {

    /** True when (focus == center)  */
    private boolean isSimpleFocus = false;

    /** True when (cycleMethod == NO_CYCLE) */
    private boolean isNonCyclic = false;

    /** Radius of the outermost circle defining the 100% gradient stop. */
    private float radius;

    /** Variables representing center and focus points. */
    private float centerX, centerY, focusX, focusY;

    /** Radius of the gradient circle squared. */
    private float radiusSq;

    /** Constant part of X, Y user space coordinates. */
    private float constA, constB;

    /** This value represents the solution when focusX == X.  It is called
     * trivial because it is easier to calculate than the general case.
     */
    private float trivial;

    private static final int FIXED_POINT_IMPL = 1;
    private static final int DEFAULT_IMPL     = 2;
    private static final int ANTI_ALIAS_IMPL  = 3;

    private int fillMethod;

    /** Amount for offset when clamping focus. */
    private static final float SCALEBACK = 0.999f;

    /**
     * Constructor for RadialGradientPaintContext.
     *
     *  @param cm {@link ColorModel} that receives
     *  the <code>Paint</code> data. This is used only as a hint.
     *
     *  @param deviceBounds the device space bounding box of the
     *  graphics primitive being rendered
     *
     *  @param userBounds the user space bounding box of the
     *  graphics primitive being rendered
     *
     *  @param t the {@link AffineTransform} from user
     *  space into device space (gradientTransform should be
     *  concatenated with this)
     *
     *  @param hints the hints that the context object uses to choose
     *  between rendering alternatives
     *
     *  @param cx the center point in user space of the circle defining
     *  the gradient.  The last color of the gradient is mapped to the
     *  perimeter of this circle X coordinate
     *
     *  @param cy the center point in user space of the circle defining
     *  the gradient.  The last color of the gradient is mapped to the
     *  perimeter of this circle Y coordinate
     *
     *  @param r the radius of the circle defining the extents of the
     *  color gradient
     *
     *  @param fx the point in user space to which the first color is mapped
     *  X coordinate
     *
     *  @param fy the point in user space to which the first color is mapped
     *  Y coordinate
     *
     *  @param fractions the fractions specifying the gradient distribution
     *
     *  @param colors the gradient colors
     *
     *  @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
     *
     *  @param colorSpace which colorspace to use for interpolation,
     *  either SRGB or LINEAR_RGB
     *
     */
    public RadialGradientPaintContext(ColorModel cm,
                                      Rectangle deviceBounds,
                                      Rectangle2D userBounds,
                                      AffineTransform t,
                                      RenderingHints hints,
                                      float cx, float cy,
                                      float r,
                                      float fx, float fy,
                                      float[] fractions,
                                      Color[] colors,
                                      MultipleGradientPaint.CycleMethodEnum
                                      cycleMethod,
                                      MultipleGradientPaint.ColorSpaceEnum
                                      colorSpace)
        throws NoninvertibleTransformException
    {
        super(cm, deviceBounds, userBounds, t, hints, fractions, colors,
              cycleMethod, colorSpace);

        //copy some parameters.
        centerX = cx;
        centerY = cy;
        focusX = fx;
        focusY = fy;
        radius = r;

        this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
        this.isNonCyclic = (cycleMethod == RadialGradientPaint.NO_CYCLE);

        //for use in the quadractic equation
        radiusSq = radius * radius;

        float dX = focusX - centerX;
        float dY = focusY - centerY;

        double dist = Math.sqrt((dX * dX) + (dY * dY));

        //test if distance from focus to center is greater than the radius
        if (dist > radius* SCALEBACK) { //clamp focus to radius
          double angle = Math.atan2(dY, dX);

          //x = r cos theta, y = r sin theta
          focusX = (float)(SCALEBACK * radius * Math.cos(angle)) + centerX;

          focusY = (float)(SCALEBACK * radius * Math.sin(angle)) + centerY;
        }

        //calculate the solution to be used in the case where X == focusX
        //in cyclicCircularGradientFillRaster
        dX = focusX - centerX;
        trivial = (float)Math.sqrt(radiusSq - (dX * dX));

        // constant parts of X, Y user space coordinates
        constA = a02 - centerX;
        constB = a12 - centerY;

        Object colorRend = hints.get(RenderingHints.KEY_COLOR_RENDERING);
        Object rend      = hints.get(RenderingHints.KEY_RENDERING);

        fillMethod = 0;

        if ((rend      == RenderingHints.VALUE_RENDER_QUALITY) ||
            (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)) {
            // System.out.println("AAHints set: " + rend + ", " + colorRend);
            fillMethod = ANTI_ALIAS_IMPL;
        }

        if ((rend      == RenderingHints.VALUE_RENDER_SPEED) ||
            (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)) {
            // System.out.println("SPHints set: " + rend + ", " + colorRend);
            fillMethod = DEFAULT_IMPL;
        }

        // We are in the 'default' case, no hint or hint set to
        // DEFAULT values...
        if (fillMethod == 0) {
            // For now we will always use the 'default' impl if
            // one is not specified.
            fillMethod = DEFAULT_IMPL;

            if (false) {
                // This could be used for a 'smart' choice in
                // the default case, if the gradient has obvious
                // discontinuites use AA, otherwise default
                if (hasDiscontinuity) {
                    fillMethod = ANTI_ALIAS_IMPL;
                } else {
                    fillMethod = DEFAULT_IMPL;
                }
            }
        }

        if ((fillMethod == DEFAULT_IMPL) &&
            (isSimpleFocus && isNonCyclic && isSimpleLookup)) {
            this.calculateFixedPointSqrtLookupTable();
            fillMethod = FIXED_POINT_IMPL;
        }
    }

    /**
     * Return a Raster containing the colors generated for the graphics
     * operation.
     *
     * @param x The x coordinate of the area in device space for which colors
     *          are generated.
     * @param y The y coordinate of the area in device space for which colors
     *          are generated.
     * @param w The width of the area in device space for which colors
     *          are generated.
     * @param h The height of the area in device space for which colors
     *          are generated.
     */
    protected void fillRaster(int[] pixels, int off, int adjust,
                              int x, int y, int w, int h) {
        switch(fillMethod) {
        case FIXED_POINT_IMPL:
            // System.out.println("Calling FP");
            fixedPointSimplestCaseNonCyclicFillRaster(pixels, off, adjust, x,
                                                      y, w, h);
            break;
        case ANTI_ALIAS_IMPL:
            // System.out.println("Calling AA");
            antiAliasFillRaster(pixels, off, adjust, x, y, w, h);
            break;
        case DEFAULT_IMPL:
        default:
            // System.out.println("Calling Default");
            cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
        }
    }

    /**
     * This code works in the simplest of cases, where the focus == center
     * point, the gradient is noncyclic, and the gradient lookup method is
     * fast (single array index, no conversion necessary).
     *
     */
    private void fixedPointSimplestCaseNonCyclicFillRaster(int[] pixels,
                                                           int off,
                                                           int adjust,
                                                           int x, int y,
                                                           int w, int h) {
        float iSq=0;  // Square distance index
        final float indexFactor = fastGradientArraySize / radius;

        //constant part of X and Y coordinates for the entire raster
        final float constX = (a00*x) + (a01*y) + constA;
        final float constY = (a10*x) + (a11*y) + constB;
        final float deltaX = indexFactor * a00; //incremental change in dX
        final float deltaY = indexFactor * a10; //incremental change in dY
        float dX, dY; //the current distance from center
        final int fixedArraySizeSq=
            (fastGradientArraySize * fastGradientArraySize);
        float g, gDelta, gDeltaDelta, temp; //gradient square value
        int gIndex; // integer number used to index gradient array
        int iSqInt; // Square distance index

        int end, j; //indexing variables
        int indexer = off;//used to index pixels array

        temp        = ((deltaX * deltaX) + (deltaY * deltaY));
        gDeltaDelta = ((temp * 2));

        if (temp > fixedArraySizeSq) {
            // This combination of scale and circle radius means
            // essentially no pixels will be anything but the end
            // stop color.  This also avoids math problems.
            final int val = gradientOverflow;
            for(j = 0; j < h; j++){ //for every row
                //for every column (inner loop begins here)
                for (end = indexer+w; indexer < end; indexer++)
                    pixels[indexer] = val;
                indexer += adjust;
            }
            return;
        }

        // For every point in the raster, calculate the color at that point
        for(j = 0; j < h; j++){ //for every row
            //x and y (in user space) of the first pixel of this row
            dX = indexFactor * ((a01*j) + constX);
            dY = indexFactor * ((a11*j) + constY);

            // these values below here allow for an incremental calculation
            // of dX^2 + dY^2

            //initialize to be equal to distance squared
            g = (((dY * dY) + (dX * dX)) );
            gDelta = (deltaY * dY + deltaX * dX) * 2 + temp;

            //for every column (inner loop begins here)
            for (end = indexer+w; indexer < end; indexer++) {
                //determine the distance to the center

                //since this is a non cyclic fill raster, crop at "1" and 0
                if (g >= fixedArraySizeSq) {
                    pixels[indexer] = gradientOverflow;
                }

                // This should not happen as gIndex is a square
                // quantity. Code commented out on purpose, can't underflow.
                // else if (g < 0) {
                //    gIndex = 0;
                // }

                else {
                    iSq = (g * invSqStepFloat);

                    iSqInt = (int)iSq; //chop off fractional part
                    iSq -= iSqInt;
                    gIndex = sqrtLutFixed[iSqInt];
                    gIndex += (int)(iSq * (sqrtLutFixed[iSqInt + 1]-gIndex));
                    pixels[indexer] = gradient[gIndex];
                }


                //incremental calculation
                g += gDelta;
                gDelta += gDeltaDelta;
            }
            indexer += adjust;
        }
    }

    /** Length of a square distance intervale in the lookup table */
    private float invSqStepFloat;

    /** Used to limit the size of the square root lookup table */
    private static final int MAX_PRECISION = 256;

    /** Square root lookup table */
    private int[] sqrtLutFixed = new int[MAX_PRECISION];

    /**
     * Build square root lookup table
     */
    private void calculateFixedPointSqrtLookupTable() {
        float sqStepFloat;
        sqStepFloat = (fastGradientArraySize * fastGradientArraySize)
                       / (MAX_PRECISION - 2.0f);

        // The last two values are the same so that linear square root
        // interpolation can happen on the maximum reachable element in the
        // lookup table (precision-2)
        int[] workTbl = sqrtLutFixed;      // local is cheaper
        int i;
        for (i = 0; i < MAX_PRECISION - 1; i++) {
            workTbl[i] = (int)Math.sqrt(i*sqStepFloat);
        }
        workTbl[i] = workTbl[i-1];
        invSqStepFloat = 1.0f/sqStepFloat;
    }

    /** Fill the raster, cycling the gradient colors when a point falls outside
     *  of the perimeter of the 100% stop circle.
     *
     *  This calculation first computes the intersection point of the line
     *  from the focus through the current point in the raster, and the
     *  perimeter of the gradient circle.
     *
     *  Then it determines the percentage distance of the current point along
     *  that line (focus is 0%, perimeter is 100%).
     *
     *  Equation of a circle centered at (a,b) with radius r:
     *  (x-a)^2 + (y-b)^2 = r^2
     *  Equation of a line with slope m and y-intercept b
     *  y = mx + b
     *  replacing y in the cirlce equation and solving using the quadratic
     *  formula produces the following set of equations.  Constant factors have
     *  been extracted out of the inner loop.
     *
     */
    private void cyclicCircularGradientFillRaster(int[] pixels, int off,
                                                  int adjust,
                                                  int x, int y,
                                                  int w, int h) {
        // Constant part of the C factor of the quadratic equation
        final double constC =
            -(radiusSq) + (centerX * centerX) + (centerY * centerY);
        double A; //coefficient of the quadratic equation (Ax^2 + Bx + C = 0)
        double B; //coefficient of the quadratic equation
        double C; //coefficient of the quadratic equation
        double slope; //slope of the focus-perimeter line
        double yintcpt; //y-intercept of the focus-perimeter line
        double solutionX;//intersection with circle X coordinate
        double solutionY;//intersection with circle Y coordinate
               final float constX = (a00*x) + (a01*y) + a02;//const part of X coord
        final float constY = (a10*x) + (a11*y) + a12; //const part of Y coord
               final float precalc2 = 2 * centerY;//const in inner loop quad. formula
        final float precalc3 =-2 * centerX;//const in inner loop quad. formula
        float X; // User space point X coordinate
        float Y; // User space point Y coordinate
        float g;//value between 0 and 1 specifying position in the gradient
        float det; //determinant of quadratic formula (should always be >0)
        float currentToFocusSq;//sq distance from the current pt. to focus
        float intersectToFocusSq;//sq distance from the intersect pt. to focus
        float deltaXSq; //temp variable for a change in X squared.
        float deltaYSq; //temp variable for a change in Y squared.
        int indexer = off; //index variable for pixels array
        int i, j; //indexing variables for FOR loops
        int pixInc = w+adjust;//incremental index change for pixels array

        for (j = 0; j < h; j++) { //for every row

            X = (a01*j) + constX; //constants from column to column
            Y = (a11*j) + constY;

            //for every column (inner loop begins here)
            for (i = 0; i < w; i++) {

                // special case to avoid divide by zero or very near zero
                if (((X-focusX)>-0.000001f) &&
                    ((X-focusX)< 0.000001f)) {
                    solutionX = focusX;

                    solutionY = centerY;

                    solutionY += (Y > focusY)?trivial:-trivial;
                }

                else {

                    //slope of the focus-current line
                    slope =   (Y - focusY) / (X - focusX);

                    yintcpt = Y - (slope * X); //y-intercept of that same line

                    //use the quadratic formula to calculate the intersection
                    //point
                    A = (slope * slope) + 1;

                    B =  precalc3 + (-2 * slope * (centerY - yintcpt));

                    C =  constC + (yintcpt* (yintcpt - precalc2));

                    det = (float)Math.sqrt((B * B) - ( 4 * A * C));

                    solutionX = -B;

                    //choose the positive or negative root depending
                    //on where the X coord lies with respect to the focus.
                    solutionX += (X < focusX)?-det:det;

                    solutionX = solutionX / (2 * A);//divisor

                    solutionY = (slope * solutionX) + yintcpt;
                }

                //calculate the square of the distance from the current point
                //to the focus and the square of the distance from the
                //intersection point to the focus. Want the squares so we can
                //do 1 square root after division instead of 2 before.

                deltaXSq = (float)solutionX - focusX;
                deltaXSq = deltaXSq * deltaXSq;

                deltaYSq = (float)solutionY - focusY;
                deltaYSq = deltaYSq * deltaYSq;

                intersectToFocusSq = deltaXSq + deltaYSq;

                deltaXSq = X - focusX;
                deltaXSq = deltaXSq * deltaXSq;

                deltaYSq = Y - focusY;
                deltaYSq = deltaYSq * deltaYSq;

                currentToFocusSq = deltaXSq + deltaYSq;

                //want the percentage (0-1) of the current point along the
                //focus-circumference line
                g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);

                //Get the color at this point
                pixels[indexer + i] = indexIntoGradientsArrays(g);

                X += a00; //incremental change in X, Y
                Y += a10;
            } //end inner loop
            indexer += pixInc;
        } //end outer loop
    }


    /** Fill the raster, cycling the gradient colors when a point
     *  falls outside of the perimeter of the 100% stop circle. Use
     *  the anti-aliased gradient lookup.
     *
     *  This calculation first computes the intersection point of the line
     *  from the focus through the current point in the raster, and the
     *  perimeter of the gradient circle.
     *
     *  Then it determines the percentage distance of the current point along
     *  that line (focus is 0%, perimeter is 100%).
     *
     *  Equation of a circle centered at (a,b) with radius r:
     *  (x-a)^2 + (y-b)^2 = r^2
     *  Equation of a line with slope m and y-intercept b
     *  y = mx + b
     *  replacing y in the cirlce equation and solving using the quadratic
     *  formula produces the following set of equations.  Constant factors have
     *  been extracted out of the inner loop.
     * */
    private void antiAliasFillRaster(int[] pixels, int off,
                                     int adjust,
                                     int x, int y,
                                     int w, int h) {
        // Constant part of the C factor of the quadratic equation
        final double constC =
            -(radiusSq) + (centerX * centerX) + (centerY * centerY);
        //coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
               final float precalc2 = 2 * centerY;//const in inner loop quad. formula
        final float precalc3 =-2 * centerX;//const in inner loop quad. formula

        //const part of X,Y coord (shifted to bottom left corner of pixel.
               final float constX = (a00*(x-.5f)) + (a01*(y+.5f)) + a02;
        final float constY = (a10*(x-.5f)) + (a11*(y+.5f)) + a12;
        float X; // User space point X coordinate
        float Y; // User space point Y coordinate
        int i, j; //indexing variables for FOR loops
        int indexer = off-1; //index variable for pixels array

        double [] prevGs = new double[w+1];
        double deltaXSq, deltaYSq;
        double solutionX, solutionY;
        double slope, yintcpt, A, B, C, det;
        double intersectToFocusSq, currentToFocusSq;
        double g00, g01, g10, g11;

        // Set X,Y to top left corner of first pixel of first row.
        X = constX - a01;
        Y = constY - a11;

        // Calc top row of g's.
        for (i=0; i <= w; i++) {
            final float dx = X - focusX;

            // special case to avoid divide by zero or very near zero
            if ( ( dx >-0.000001f ) &&
                 ( dx < 0.000001f ))  {
                solutionX = focusX;
                solutionY = centerY;
                solutionY += (Y > focusY)?trivial:-trivial;
            }
            else {
                // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
                // Formula line:        Y = Slope*x + Y0;
                //
                // So you substitue line into Circle and apply
                // Quadradic formula.


                //slope of the focus-current line
                slope =   (Y - focusY) / (X - focusX);

                yintcpt = Y - (slope * X); //y-intercept of that same line

                //use the quadratic formula to calculate the intersection
                //point
                A = (slope * slope) + 1;

                B =  precalc3 + (-2 * slope * (centerY - yintcpt));

                C =  constC + (yintcpt* (yintcpt - precalc2));

                det = Math.sqrt((B * B) - ( 4 * A * C));

                solutionX = -B;

                //choose the positive or negative root depending
                //on where the X coord lies with respect to the focus.
                solutionX += (X < focusX)?-det:det;

                solutionX = solutionX / (2 * A);//divisor

                solutionY = (slope * solutionX) + yintcpt;
            }

            //calculate the square of the distance from the current point
            //to the focus and the square of the distance from the
            //intersection point to the focus. Want the squares so we can
            //do 1 square root after division instead of 2 before.
            deltaXSq = solutionX - focusX;
            deltaXSq = deltaXSq * deltaXSq;

            deltaYSq = solutionY - focusY;
            deltaYSq = deltaYSq * deltaYSq;

            intersectToFocusSq = deltaXSq + deltaYSq;

            deltaXSq = X - focusX;
            deltaXSq = deltaXSq * deltaXSq;

            deltaYSq = Y - focusY;
            deltaYSq = deltaYSq * deltaYSq;

            currentToFocusSq = deltaXSq + deltaYSq;

            //want the percentage (0-1) of the current point along the
            //focus-circumference line
            prevGs[i] = Math.sqrt(currentToFocusSq / intersectToFocusSq);

            X += a00; //incremental change in X, Y
            Y += a10;
        }

        for (j = 0; j < h; j++) { //for every row

            // Set X,Y to bottom edge of pixel row.
            X = (a01*j) + constX; //constants from row to row
            Y = (a11*j) + constY;

            g10 = prevGs[0];

            float dx = X - focusX;
            // special case to avoid divide by zero or very near zero
            if ( ( dx >-0.000001f ) &&
                 ( dx < 0.000001f ))  {
                solutionX = focusX;
                solutionY = centerY;
                solutionY += (Y > focusY)?trivial:-trivial;
            }
            else {
                // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
                // Formula line:        Y = Slope*x + Y0;
                //
                // So you substitue line into Circle and apply
                // Quadradic formula.


                //slope of the focus-current line
                slope =   (Y - focusY) / (X - focusX);

                yintcpt = Y - (slope * X); //y-intercept of that same line

                //use the quadratic formula to calculate the intersection
                //point
                A = (slope * slope) + 1;

                B =  precalc3 + (-2 * slope * (centerY - yintcpt));

                C =  constC + (yintcpt* (yintcpt - precalc2));

                det = Math.sqrt((B * B) - ( 4 * A * C));

                solutionX = -B;

                //choose the positive or negative root depending
                //on where the X coord lies with respect to the focus.
                solutionX += (X < focusX)?-det:det;

                solutionX = solutionX / (2 * A);//divisor

                solutionY = (slope * solutionX) + yintcpt;
            }

            //calculate the square of the distance from the current point
            //to the focus and the square of the distance from the
            //intersection point to the focus. Want the squares so we can
            //do 1 square root after division instead of 2 before.
            deltaXSq = solutionX - focusX;
            deltaXSq = deltaXSq * deltaXSq;

            deltaYSq = solutionY - focusY;
            deltaYSq = deltaYSq * deltaYSq;

            intersectToFocusSq = deltaXSq + deltaYSq;

            deltaXSq = X - focusX;
            deltaXSq = deltaXSq * deltaXSq;

            deltaYSq = Y - focusY;
            deltaYSq = deltaYSq * deltaYSq;

            currentToFocusSq = deltaXSq + deltaYSq;
            g11 = Math.sqrt(currentToFocusSq / intersectToFocusSq);
            prevGs[0] = g11;

            X += a00; //incremental change in X, Y
            Y += a10;

            //for every column (inner loop begins here)
            for (i=1; i <= w; i++) {
                g00 = g10;
                g01 = g11;
                g10 = prevGs[i];

                dx = X - focusX;
                // special case to avoid divide by zero or very near zero
                if ( ( dx >-0.000001f ) &&
                     ( dx < 0.000001f ))  {
                    solutionX = focusX;
                    solutionY = centerY;
                    solutionY += (Y > focusY)?trivial:-trivial;
                }
                else {
                    // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
                    // Formula line:        Y = Slope*x + Y0;
                    //
                    // So you substitue line into Circle and apply
                    // Quadradic formula.


                    //slope of the focus-current line
                    slope =   (Y - focusY) / (X - focusX);

                    yintcpt = Y - (slope * X); //y-intercept of that same line

                    //use the quadratic formula to calculate the intersection
                    //point
                    A = (slope * slope) + 1;

                    B =  precalc3 + (-2 * slope * (centerY - yintcpt));

                    C =  constC + (yintcpt* (yintcpt - precalc2));

                    det = Math.sqrt((B * B) - ( 4 * A * C));

                    solutionX = -B;

                    //choose the positive or negative root depending
                    //on where the X coord lies with respect to the focus.
                    solutionX += (X < focusX)?-det:det;

                    solutionX = solutionX / (2 * A);//divisor

                    solutionY = (slope * solutionX) + yintcpt;
                }

                //calculate the square of the distance from the current point
                //to the focus and the square of the distance from the
                //intersection point to the focus. Want the squares so we can
                //do 1 square root after division instead of 2 before.
                deltaXSq = solutionX - focusX;
                deltaXSq = deltaXSq * deltaXSq;

                deltaYSq = solutionY - focusY;
                deltaYSq = deltaYSq * deltaYSq;

                intersectToFocusSq = deltaXSq + deltaYSq;

                deltaXSq = X - focusX;
                deltaXSq = deltaXSq * deltaXSq;

                deltaYSq = Y - focusY;
                deltaYSq = deltaYSq * deltaYSq;

                currentToFocusSq = deltaXSq + deltaYSq;
                g11 = Math.sqrt(currentToFocusSq / intersectToFocusSq);
                prevGs[i] = g11;

                //Get the color at this point
                pixels[indexer+i] = indexGradientAntiAlias
                    ((float)((g00+g01+g10+g11)/4),
                     (float)Math.max(Math.abs(g11-g00),
                                     Math.abs(g10-g01)));

                X += a00; //incremental change in X, Y
                Y += a10;
            } //end inner loop
            indexer += (w+adjust);
        } //end outer loop
    }
}
