/*

   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, Vincent Hardy
Version:$Id: RadialGradientPaintContext.java 1733416 2016-03-03 07:07:13Z gadams $
/** * 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)
/** True when (focus == center) */
private boolean isSimpleFocus = false;
True when (cycleMethod == NO_CYCLE)
/** True when (cycleMethod == NO_CYCLE) */
private boolean isNonCyclic = false;
Radius of the outermost circle defining the 100% gradient stop.
/** Radius of the outermost circle defining the 100% gradient stop. */
private float radius;
Variables representing center and focus points.
/** Variables representing center and focus points. */
private float centerX, centerY, focusX, focusY;
Radius of the gradient circle squared.
/** Radius of the gradient circle squared. */
private float radiusSq;
Constant part of X, Y user space coordinates.
/** 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.
/** 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.
/** Amount for offset when clamping focus. */
private static final float SCALEBACK = 0.999f;
Constructor for RadialGradientPaintContext. @param cm ColorModel that receives the Paint 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 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
/** * 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.
Params:
  • x – The x coordinate of the area in device space for which colors are generated.
  • y – The y coordinate of the area in device space for which colors are generated.
  • w – The width of the area in device space for which colors are generated.
  • h – The height of the area in device space for which colors are generated.
/** * 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).
/** * 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
/** Length of a square distance intervale in the lookup table */
private float invSqStepFloat;
Used to limit the size of the square root lookup table
/** Used to limit the size of the square root lookup table */
private static final int MAX_PRECISION = 256;
Square root lookup table
/** Square root lookup table */
private int[] sqrtLutFixed = new int[MAX_PRECISION];
Build square root lookup table
/** * 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.
/** 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.
/** 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 } }