/** Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************/package java.awt;import java.awt.MultipleGradientPaint.CycleMethod;import java.awt.MultipleGradientPaint.ColorSpaceType;import java.awt.geom.AffineTransform;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*/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;/** Constant second order delta for simple loop. */private float gDeltaDelta;/*** 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;/** Amount for offset when clamping focus. */private static final float SCALEBACK = .99f;/*** Constructor for RadialGradientPaintContext.** @param paint the {@code RadialGradientPaint} from which this context* is created* @param cm the {@code ColorModel} that receives* the {@code 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 {@code 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 X coordinate in user space of the circle defining* the gradient. The last color of the gradient is mapped to* the perimeter of this circle.* @param cy the center Y coordinate in user space of the circle defining* the gradient. The last color of the gradient is mapped to* the perimeter of this circle.* @param r the radius of the circle defining the extents of the* color gradient* @param fx the X coordinate in user space to which the first color* is mapped* @param fy the Y coordinate in user space to which the first color* is mapped* @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*/RadialGradientPaintContext(RadialGradientPaint paint,ColorModel cm,Rectangle deviceBounds,Rectangle2D userBounds,AffineTransform t,RenderingHints hints,float cx, float cy,float r,float fx, float fy,float[] fractions,Color[] colors,CycleMethod cycleMethod,ColorSpaceType colorSpace){super(paint, cm, deviceBounds, userBounds, t, hints,fractions, colors, cycleMethod, colorSpace);// copy some parameterscenterX = cx;centerY = cy;focusX = fx;focusY = fy;radius = r;this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);this.isNonCyclic = (cycleMethod == CycleMethod.NO_CYCLE);// for use in the quadractic equationradiusSq = radius * radius;float dX = focusX - centerX;float dY = focusY - centerY;double distSq = (dX * dX) + (dY * dY);// test if distance from focus to center is greater than the radiusif (distSq > radiusSq * SCALEBACK) {// clamp focus to radiusfloat scalefactor = (float)Math.sqrt(radiusSq * SCALEBACK / distSq);dX = dX * scalefactor;dY = dY * scalefactor;focusX = centerX + dX;focusY = centerY + dY;}// calculate the solution to be used in the case where X == focusX// in cyclicCircularGradientFillRaster()trivial = (float)Math.sqrt(radiusSq - (dX * dX));// constant parts of X, Y user space coordinatesconstA = a02 - centerX;constB = a12 - centerY;// constant second order delta for simple loopgDeltaDelta = 2 * ( a00 * a00 + a10 * a10) / radiusSq;}/*** Return a Raster containing the colors generated for the graphics* operation.** @param x,y,w,h 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){if (isSimpleFocus && isNonCyclic && isSimpleLookup) {simpleNonCyclicFillRaster(pixels, off, adjust, x, y, w, h);} else {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 simpleNonCyclicFillRaster(int pixels[], int off, int adjust,int x, int y, int w, int h){/* We calculate sqrt(X^2 + Y^2) relative to the radius* size to get the fraction for the color to use.** Each step along the scanline adds (a00, a10) to (X, Y).* If we precalculate:* gRel = X^2+Y^2* for the start of the row, then for each step we need to* calculate:* gRel' = (X+a00)^2 + (Y+a10)^2* = X^2 + 2*X*a00 + a00^2 + Y^2 + 2*Y*a10 + a10^2* = (X^2+Y^2) + 2*(X*a00+Y*a10) + (a00^2+a10^2)* = gRel + 2*(X*a00+Y*a10) + (a00^2+a10^2)* = gRel + 2*DP + SD* (where DP = dot product between X,Y and a00,a10* and SD = dot product square of the delta vector)* For the step after that we get:* gRel'' = (X+2*a00)^2 + (Y+2*a10)^2* = X^2 + 4*X*a00 + 4*a00^2 + Y^2 + 4*Y*a10 + 4*a10^2* = (X^2+Y^2) + 4*(X*a00+Y*a10) + 4*(a00^2+a10^2)* = gRel + 4*DP + 4*SD* = gRel' + 2*DP + 3*SD* The increment changed by:* (gRel'' - gRel') - (gRel' - gRel)* = (2*DP + 3*SD) - (2*DP + SD)* = 2*SD* Note that this value depends only on the (inverse of the)* transformation matrix and so is a constant for the loop.* To make this all relative to the unit circle, we need to* divide all values as follows:* [XY] /= radius* gRel /= radiusSq* DP /= radiusSq* SD /= radiusSq*/// coordinates of UL corner in "user space" relative to centerfloat rowX = (a00*x) + (a01*y) + constA;float rowY = (a10*x) + (a11*y) + constB;// second order delta calculated in constructorfloat gDeltaDelta = this.gDeltaDelta;// adjust is (scan-w) of pixels array, we need (scan)adjust += w;// rgb of the 1.0 color used when the distance exceeds gradient radiusint rgbclip = gradient[fastGradientArraySize];for (int j = 0; j < h; j++) {// these values depend on the coordinates of the start of the rowfloat gRel = (rowX * rowX + rowY * rowY) / radiusSq;float gDelta = (2 * ( a00 * rowX + a10 * rowY) / radiusSq +gDeltaDelta/2);/* Use optimized loops for any cases where gRel >= 1.* We do not need to calculate sqrt(gRel) for these* values since sqrt(N>=1) == (M>=1).* Note that gRel follows a parabola which can only be < 1* for a small region around the center on each scanline. In* particular:* gDeltaDelta is always positive* gDelta is <0 until it crosses the midpoint, then >0* To the left and right of that region, it will always be* >=1 out to infinity, so we can process the line in 3* regions:* out to the left - quick fill until gRel < 1, updating gRel* in the heart - slow fraction=sqrt fill while gRel < 1* out to the right - quick fill rest of scanline, ignore gRel*/int i = 0;// Quick fill for "out to the left"while (i < w && gRel >= 1.0f) {pixels[off + i] = rgbclip;gRel += gDelta;gDelta += gDeltaDelta;i++;}// Slow fill for "in the heart"while (i < w && gRel < 1.0f) {int gIndex;if (gRel <= 0) {gIndex = 0;} else {float fIndex = gRel * SQRT_LUT_SIZE;int iIndex = (int) (fIndex);float s0 = sqrtLut[iIndex];float s1 = sqrtLut[iIndex+1] - s0;fIndex = s0 + (fIndex - iIndex) * s1;gIndex = (int) (fIndex * fastGradientArraySize);}// store the color at this pointpixels[off + i] = gradient[gIndex];// incremental calculationgRel += gDelta;gDelta += gDeltaDelta;i++;}// Quick fill to end of line for "out to the right"while (i < w) {pixels[off + i] = rgbclip;i++;}off += adjust;rowX += a01;rowY += a11;}}// SQRT_LUT_SIZE must be a power of 2 for the test above to work.private static final int SQRT_LUT_SIZE = (1 << 11);private static float sqrtLut[] = new float[SQRT_LUT_SIZE+1];static {for (int i = 0; i < sqrtLut.length; i++) {sqrtLut[i] = (float) Math.sqrt(i / ((float) SQRT_LUT_SIZE));}}/*** 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 circle 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 equationfinal double constC =-radiusSq + (centerX * centerX) + (centerY * centerY);// coefficients of the quadratic equation (Ax^2 + Bx + C = 0)double A, B, C;// slope and y-intercept of the focus-perimeter linedouble slope, yintcpt;// intersection with circle X,Y coordinatedouble solutionX, solutionY;// constant parts of X, Y coordinatesfinal float constX = (a00*x) + (a01*y) + a02;final float constY = (a10*x) + (a11*y) + a12;// constants in inner loop quadratic formulafinal float precalc2 = 2 * centerY;final float precalc3 = -2 * centerX;// value between 0 and 1 specifying position in the gradientfloat g;// determinant of quadratic formula (should always be > 0)float det;// sq distance from the current point to focusfloat currentToFocusSq;// sq distance from the intersect point to focusfloat intersectToFocusSq;// temp variables for change in X,Y squaredfloat deltaXSq, deltaYSq;// used to index pixels arrayint indexer = off;// incremental index change for pixels arrayint pixInc = w+adjust;// for every rowfor (int j = 0; j < h; j++) {// user space point; these are constant from column to columnfloat X = (a01*j) + constX;float Y = (a11*j) + constY;// for every column (inner loop begins here)for (int i = 0; i < w; i++) {if (X == focusX) {// special case to avoid divide by zerosolutionX = focusX;solutionY = centerY;solutionY += (Y > focusY) ? trivial : -trivial;} else {// slope and y-intercept of the focus-perimeter lineslope = (Y - focusY) / (X - focusX);yintcpt = Y - (slope * X);// use the quadratic formula to calculate the// intersection pointA = (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 focussolutionX += (X < focusX)? -det : det;solutionX = solutionX / (2 * A); // divisorsolutionY = (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 = X - focusX;deltaXSq = deltaXSq * deltaXSq;deltaYSq = Y - focusY;deltaYSq = deltaYSq * deltaYSq;currentToFocusSq = deltaXSq + deltaYSq;deltaXSq = (float)solutionX - focusX;deltaXSq = deltaXSq * deltaXSq;deltaYSq = (float)solutionY - focusY;deltaYSq = deltaYSq * deltaYSq;intersectToFocusSq = deltaXSq + deltaYSq;// get the percentage (0-1) of the current point along the// focus-circumference lineg = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);// store the color at this pointpixels[indexer + i] = indexIntoGradientsArrays(g);// incremental change in X, YX += a00;Y += a10;} //end inner loopindexer += pixInc;} //end outer loop}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。