Home » openjdk-7 » java » awt » [javadoc | source]

    1   /*
    2    * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package java.awt;
   27   
   28   import java.awt.MultipleGradientPaint.CycleMethod;
   29   import java.awt.MultipleGradientPaint.ColorSpaceType;
   30   import java.awt.geom.AffineTransform;
   31   import java.awt.geom.Rectangle2D;
   32   import java.awt.image.ColorModel;
   33   
   34   /**
   35    * Provides the actual implementation for the RadialGradientPaint.
   36    * This is where the pixel processing is done.  A RadialGradienPaint
   37    * only supports circular gradients, but it should be possible to scale
   38    * the circle to look approximately elliptical, by means of a
   39    * gradient transform passed into the RadialGradientPaint constructor.
   40    *
   41    * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
   42    */
   43   final class RadialGradientPaintContext extends MultipleGradientPaintContext {
   44   
   45       /** True when (focus == center).  */
   46       private boolean isSimpleFocus = false;
   47   
   48       /** True when (cycleMethod == NO_CYCLE). */
   49       private boolean isNonCyclic = false;
   50   
   51       /** Radius of the outermost circle defining the 100% gradient stop. */
   52       private float radius;
   53   
   54       /** Variables representing center and focus points. */
   55       private float centerX, centerY, focusX, focusY;
   56   
   57       /** Radius of the gradient circle squared. */
   58       private float radiusSq;
   59   
   60       /** Constant part of X, Y user space coordinates. */
   61       private float constA, constB;
   62   
   63       /** Constant second order delta for simple loop. */
   64       private float gDeltaDelta;
   65   
   66       /**
   67        * This value represents the solution when focusX == X.  It is called
   68        * trivial because it is easier to calculate than the general case.
   69        */
   70       private float trivial;
   71   
   72       /** Amount for offset when clamping focus. */
   73       private static final float SCALEBACK = .99f;
   74   
   75       /**
   76        * Constructor for RadialGradientPaintContext.
   77        *
   78        * @param paint the {@code RadialGradientPaint} from which this context
   79        *              is created
   80        * @param cm the {@code ColorModel} that receives
   81        *           the {@code Paint} data (this is used only as a hint)
   82        * @param deviceBounds the device space bounding box of the
   83        *                     graphics primitive being rendered
   84        * @param userBounds the user space bounding box of the
   85        *                   graphics primitive being rendered
   86        * @param t the {@code AffineTransform} from user
   87        *          space into device space (gradientTransform should be
   88        *          concatenated with this)
   89        * @param hints the hints that the context object uses to choose
   90        *              between rendering alternatives
   91        * @param cx the center X coordinate in user space of the circle defining
   92        *           the gradient.  The last color of the gradient is mapped to
   93        *           the perimeter of this circle.
   94        * @param cy the center Y coordinate in user space of the circle defining
   95        *           the gradient.  The last color of the gradient is mapped to
   96        *           the perimeter of this circle.
   97        * @param r the radius of the circle defining the extents of the
   98        *          color gradient
   99        * @param fx the X coordinate in user space to which the first color
  100        *           is mapped
  101        * @param fy the Y coordinate in user space to which the first color
  102        *           is mapped
  103        * @param fractions the fractions specifying the gradient distribution
  104        * @param colors the gradient colors
  105        * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
  106        * @param colorSpace which colorspace to use for interpolation,
  107        *                   either SRGB or LINEAR_RGB
  108        */
  109       RadialGradientPaintContext(RadialGradientPaint paint,
  110                                  ColorModel cm,
  111                                  Rectangle deviceBounds,
  112                                  Rectangle2D userBounds,
  113                                  AffineTransform t,
  114                                  RenderingHints hints,
  115                                  float cx, float cy,
  116                                  float r,
  117                                  float fx, float fy,
  118                                  float[] fractions,
  119                                  Color[] colors,
  120                                  CycleMethod cycleMethod,
  121                                  ColorSpaceType colorSpace)
  122       {
  123           super(paint, cm, deviceBounds, userBounds, t, hints,
  124                 fractions, colors, cycleMethod, colorSpace);
  125   
  126           // copy some parameters
  127           centerX = cx;
  128           centerY = cy;
  129           focusX = fx;
  130           focusY = fy;
  131           radius = r;
  132   
  133           this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
  134           this.isNonCyclic = (cycleMethod == CycleMethod.NO_CYCLE);
  135   
  136           // for use in the quadractic equation
  137           radiusSq = radius * radius;
  138   
  139           float dX = focusX - centerX;
  140           float dY = focusY - centerY;
  141   
  142           double distSq = (dX * dX) + (dY * dY);
  143   
  144           // test if distance from focus to center is greater than the radius
  145           if (distSq > radiusSq * SCALEBACK) {
  146               // clamp focus to radius
  147               float scalefactor = (float)Math.sqrt(radiusSq * SCALEBACK / distSq);
  148               dX = dX * scalefactor;
  149               dY = dY * scalefactor;
  150               focusX = centerX + dX;
  151               focusY = centerY + dY;
  152           }
  153   
  154           // calculate the solution to be used in the case where X == focusX
  155           // in cyclicCircularGradientFillRaster()
  156           trivial = (float)Math.sqrt(radiusSq - (dX * dX));
  157   
  158           // constant parts of X, Y user space coordinates
  159           constA = a02 - centerX;
  160           constB = a12 - centerY;
  161   
  162           // constant second order delta for simple loop
  163           gDeltaDelta = 2 * ( a00 *  a00 +  a10 *  a10) / radiusSq;
  164       }
  165   
  166       /**
  167        * Return a Raster containing the colors generated for the graphics
  168        * operation.
  169        *
  170        * @param x,y,w,h the area in device space for which colors are
  171        * generated.
  172        */
  173       protected void fillRaster(int pixels[], int off, int adjust,
  174                                 int x, int y, int w, int h)
  175       {
  176           if (isSimpleFocus && isNonCyclic && isSimpleLookup) {
  177               simpleNonCyclicFillRaster(pixels, off, adjust, x, y, w, h);
  178           } else {
  179               cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
  180           }
  181       }
  182   
  183       /**
  184        * This code works in the simplest of cases, where the focus == center
  185        * point, the gradient is noncyclic, and the gradient lookup method is
  186        * fast (single array index, no conversion necessary).
  187        */
  188       private void simpleNonCyclicFillRaster(int pixels[], int off, int adjust,
  189                                              int x, int y, int w, int h)
  190       {
  191           /* We calculate sqrt(X^2 + Y^2) relative to the radius
  192            * size to get the fraction for the color to use.
  193            *
  194            * Each step along the scanline adds (a00, a10) to (X, Y).
  195            * If we precalculate:
  196            *   gRel = X^2+Y^2
  197            * for the start of the row, then for each step we need to
  198            * calculate:
  199            *   gRel' = (X+a00)^2 + (Y+a10)^2
  200            *         = X^2 + 2*X*a00 + a00^2 + Y^2 + 2*Y*a10 + a10^2
  201            *         = (X^2+Y^2) + 2*(X*a00+Y*a10) + (a00^2+a10^2)
  202            *         = gRel + 2*(X*a00+Y*a10) + (a00^2+a10^2)
  203            *         = gRel + 2*DP + SD
  204            * (where DP = dot product between X,Y and a00,a10
  205            *  and   SD = dot product square of the delta vector)
  206            * For the step after that we get:
  207            *   gRel'' = (X+2*a00)^2 + (Y+2*a10)^2
  208            *          = X^2 + 4*X*a00 + 4*a00^2 + Y^2 + 4*Y*a10 + 4*a10^2
  209            *          = (X^2+Y^2) + 4*(X*a00+Y*a10) + 4*(a00^2+a10^2)
  210            *          = gRel  + 4*DP + 4*SD
  211            *          = gRel' + 2*DP + 3*SD
  212            * The increment changed by:
  213            *     (gRel'' - gRel') - (gRel' - gRel)
  214            *   = (2*DP + 3*SD) - (2*DP + SD)
  215            *   = 2*SD
  216            * Note that this value depends only on the (inverse of the)
  217            * transformation matrix and so is a constant for the loop.
  218            * To make this all relative to the unit circle, we need to
  219            * divide all values as follows:
  220            *   [XY] /= radius
  221            *   gRel /= radiusSq
  222            *   DP   /= radiusSq
  223            *   SD   /= radiusSq
  224            */
  225           // coordinates of UL corner in "user space" relative to center
  226           float rowX = (a00*x) + (a01*y) + constA;
  227           float rowY = (a10*x) + (a11*y) + constB;
  228   
  229           // second order delta calculated in constructor
  230           float gDeltaDelta = this.gDeltaDelta;
  231   
  232           // adjust is (scan-w) of pixels array, we need (scan)
  233           adjust += w;
  234   
  235           // rgb of the 1.0 color used when the distance exceeds gradient radius
  236           int rgbclip = gradient[fastGradientArraySize];
  237   
  238           for (int j = 0; j < h; j++) {
  239               // these values depend on the coordinates of the start of the row
  240               float gRel   =      (rowX * rowX + rowY * rowY) / radiusSq;
  241               float gDelta = (2 * ( a00 * rowX +  a10 * rowY) / radiusSq +
  242                               gDeltaDelta/2);
  243   
  244               /* Use optimized loops for any cases where gRel >= 1.
  245                * We do not need to calculate sqrt(gRel) for these
  246                * values since sqrt(N>=1) == (M>=1).
  247                * Note that gRel follows a parabola which can only be < 1
  248                * for a small region around the center on each scanline. In
  249                * particular:
  250                *   gDeltaDelta is always positive
  251                *   gDelta is <0 until it crosses the midpoint, then >0
  252                * To the left and right of that region, it will always be
  253                * >=1 out to infinity, so we can process the line in 3
  254                * regions:
  255                *   out to the left  - quick fill until gRel < 1, updating gRel
  256                *   in the heart     - slow fraction=sqrt fill while gRel < 1
  257                *   out to the right - quick fill rest of scanline, ignore gRel
  258                */
  259               int i = 0;
  260               // Quick fill for "out to the left"
  261               while (i < w && gRel >= 1.0f) {
  262                   pixels[off + i] = rgbclip;
  263                   gRel += gDelta;
  264                   gDelta += gDeltaDelta;
  265                   i++;
  266               }
  267               // Slow fill for "in the heart"
  268               while (i < w && gRel < 1.0f) {
  269                   int gIndex;
  270   
  271                   if (gRel <= 0) {
  272                       gIndex = 0;
  273                   } else {
  274                       float fIndex = gRel * SQRT_LUT_SIZE;
  275                       int iIndex = (int) (fIndex);
  276                       float s0 = sqrtLut[iIndex];
  277                       float s1 = sqrtLut[iIndex+1] - s0;
  278                       fIndex = s0 + (fIndex - iIndex) * s1;
  279                       gIndex = (int) (fIndex * fastGradientArraySize);
  280                   }
  281   
  282                   // store the color at this point
  283                   pixels[off + i] = gradient[gIndex];
  284   
  285                   // incremental calculation
  286                   gRel += gDelta;
  287                   gDelta += gDeltaDelta;
  288                   i++;
  289               }
  290               // Quick fill to end of line for "out to the right"
  291               while (i < w) {
  292                   pixels[off + i] = rgbclip;
  293                   i++;
  294               }
  295   
  296               off += adjust;
  297               rowX += a01;
  298               rowY += a11;
  299           }
  300       }
  301   
  302       // SQRT_LUT_SIZE must be a power of 2 for the test above to work.
  303       private static final int SQRT_LUT_SIZE = (1 << 11);
  304       private static float sqrtLut[] = new float[SQRT_LUT_SIZE+1];
  305       static {
  306           for (int i = 0; i < sqrtLut.length; i++) {
  307               sqrtLut[i] = (float) Math.sqrt(i / ((float) SQRT_LUT_SIZE));
  308           }
  309       }
  310   
  311       /**
  312        * Fill the raster, cycling the gradient colors when a point falls outside
  313        * of the perimeter of the 100% stop circle.
  314        *
  315        * This calculation first computes the intersection point of the line
  316        * from the focus through the current point in the raster, and the
  317        * perimeter of the gradient circle.
  318        *
  319        * Then it determines the percentage distance of the current point along
  320        * that line (focus is 0%, perimeter is 100%).
  321        *
  322        * Equation of a circle centered at (a,b) with radius r:
  323        *     (x-a)^2 + (y-b)^2 = r^2
  324        * Equation of a line with slope m and y-intercept b:
  325        *     y = mx + b
  326        * Replacing y in the circle equation and solving using the quadratic
  327        * formula produces the following set of equations.  Constant factors have
  328        * been extracted out of the inner loop.
  329        */
  330       private void cyclicCircularGradientFillRaster(int pixels[], int off,
  331                                                     int adjust,
  332                                                     int x, int y,
  333                                                     int w, int h)
  334       {
  335           // constant part of the C factor of the quadratic equation
  336           final double constC =
  337               -radiusSq + (centerX * centerX) + (centerY * centerY);
  338   
  339           // coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
  340           double A, B, C;
  341   
  342           // slope and y-intercept of the focus-perimeter line
  343           double slope, yintcpt;
  344   
  345           // intersection with circle X,Y coordinate
  346           double solutionX, solutionY;
  347   
  348           // constant parts of X, Y coordinates
  349           final float constX = (a00*x) + (a01*y) + a02;
  350           final float constY = (a10*x) + (a11*y) + a12;
  351   
  352           // constants in inner loop quadratic formula
  353           final float precalc2 =  2 * centerY;
  354           final float precalc3 = -2 * centerX;
  355   
  356           // value between 0 and 1 specifying position in the gradient
  357           float g;
  358   
  359           // determinant of quadratic formula (should always be > 0)
  360           float det;
  361   
  362           // sq distance from the current point to focus
  363           float currentToFocusSq;
  364   
  365           // sq distance from the intersect point to focus
  366           float intersectToFocusSq;
  367   
  368           // temp variables for change in X,Y squared
  369           float deltaXSq, deltaYSq;
  370   
  371           // used to index pixels array
  372           int indexer = off;
  373   
  374           // incremental index change for pixels array
  375           int pixInc = w+adjust;
  376   
  377           // for every row
  378           for (int j = 0; j < h; j++) {
  379   
  380               // user space point; these are constant from column to column
  381               float X = (a01*j) + constX;
  382               float Y = (a11*j) + constY;
  383   
  384               // for every column (inner loop begins here)
  385               for (int i = 0; i < w; i++) {
  386   
  387                   if (X == focusX) {
  388                       // special case to avoid divide by zero
  389                       solutionX = focusX;
  390                       solutionY = centerY;
  391                       solutionY += (Y > focusY) ? trivial : -trivial;
  392                   } else {
  393                       // slope and y-intercept of the focus-perimeter line
  394                       slope = (Y - focusY) / (X - focusX);
  395                       yintcpt = Y - (slope * X);
  396   
  397                       // use the quadratic formula to calculate the
  398                       // intersection point
  399                       A = (slope * slope) + 1;
  400                       B = precalc3 + (-2 * slope * (centerY - yintcpt));
  401                       C = constC + (yintcpt* (yintcpt - precalc2));
  402   
  403                       det = (float)Math.sqrt((B * B) - (4 * A * C));
  404                       solutionX = -B;
  405   
  406                       // choose the positive or negative root depending
  407                       // on where the X coord lies with respect to the focus
  408                       solutionX += (X < focusX)? -det : det;
  409                       solutionX = solutionX / (2 * A); // divisor
  410                       solutionY = (slope * solutionX) + yintcpt;
  411                   }
  412   
  413                   // Calculate the square of the distance from the current point
  414                   // to the focus and the square of the distance from the
  415                   // intersection point to the focus. Want the squares so we can
  416                   // do 1 square root after division instead of 2 before.
  417   
  418                   deltaXSq = X - focusX;
  419                   deltaXSq = deltaXSq * deltaXSq;
  420   
  421                   deltaYSq = Y - focusY;
  422                   deltaYSq = deltaYSq * deltaYSq;
  423   
  424                   currentToFocusSq = deltaXSq + deltaYSq;
  425   
  426                   deltaXSq = (float)solutionX - focusX;
  427                   deltaXSq = deltaXSq * deltaXSq;
  428   
  429                   deltaYSq = (float)solutionY - focusY;
  430                   deltaYSq = deltaYSq * deltaYSq;
  431   
  432                   intersectToFocusSq = deltaXSq + deltaYSq;
  433   
  434                   // get the percentage (0-1) of the current point along the
  435                   // focus-circumference line
  436                   g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);
  437   
  438                   // store the color at this point
  439                   pixels[indexer + i] = indexIntoGradientsArrays(g);
  440   
  441                   // incremental change in X, Y
  442                   X += a00;
  443                   Y += a10;
  444               } //end inner loop
  445   
  446               indexer += pixInc;
  447           } //end outer loop
  448       }
  449   }

Home » openjdk-7 » java » awt » [javadoc | source]