1 /*
   2  * Copyright (c) 2006, 2013, 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.geom.AffineTransform;
  29 import java.awt.geom.Point2D;
  30 import java.awt.geom.Rectangle2D;
  31 import java.awt.image.ColorModel;
  32 import java.beans.ConstructorProperties;
  33 
  34 /**
  35  * The {@code LinearGradientPaint} class provides a way to fill
  36  * a {@link java.awt.Shape} with a linear color gradient pattern.  The user
  37  * may specify two or more gradient colors, and this paint will provide an
  38  * interpolation between each color.  The user also specifies start and end
  39  * points which define where in user space the color gradient should begin
  40  * and end.
  41  * <p>
  42  * The user must provide an array of floats specifying how to distribute the
  43  * colors along the gradient.  These values should range from 0.0 to 1.0 and
  44  * act like keyframes along the gradient (they mark where the gradient should
  45  * be exactly a particular color).
  46  * <p>
  47  * In the event that the user does not set the first keyframe value equal
  48  * to 0 and/or the last keyframe value equal to 1, keyframes will be created
  49  * at these positions and the first and last colors will be replicated there.
  50  * So, if a user specifies the following arrays to construct a gradient:<br>
  51  * <pre>
  52  *     {Color.BLUE, Color.RED}, {.3f, .7f}
  53  * </pre>
  54  * this will be converted to a gradient with the following keyframes:<br>
  55  * <pre>
  56  *     {Color.BLUE, Color.BLUE, Color.RED, Color.RED}, {0f, .3f, .7f, 1f}
  57  * </pre>
  58  *
  59  * <p>
  60  * The user may also select what action the {@code LinearGradientPaint} object
  61  * takes when it is filling the space outside the start and end points by
  62  * setting {@code CycleMethod} to either {@code REFLECTION} or {@code REPEAT}.
  63  * The distances between any two colors in any of the reflected or repeated
  64  * copies of the gradient are the same as the distance between those same two
  65  * colors between the start and end points.
  66  * Note that some minor variations in distances may occur due to sampling at
  67  * the granularity of a pixel.
  68  * If no cycle method is specified, {@code NO_CYCLE} will be chosen by
  69  * default, which means the endpoint colors will be used to fill the
  70  * remaining area.
  71  * <p>
  72  * The colorSpace parameter allows the user to specify in which colorspace
  73  * the interpolation should be performed, default sRGB or linearized RGB.
  74  *
  75  * <p>
  76  * The following code demonstrates typical usage of
  77  * {@code LinearGradientPaint}:
  78  * <pre>
  79  *     Point2D start = new Point2D.Float(0, 0);
  80  *     Point2D end = new Point2D.Float(50, 50);
  81  *     float[] dist = {0.0f, 0.2f, 1.0f};
  82  *     Color[] colors = {Color.RED, Color.WHITE, Color.BLUE};
  83  *     LinearGradientPaint p =
  84  *         new LinearGradientPaint(start, end, dist, colors);
  85  * </pre>
  86  * <p>
  87  * This code will create a {@code LinearGradientPaint} which interpolates
  88  * between red and white for the first 20% of the gradient and between white
  89  * and blue for the remaining 80%.
  90  *
  91  * <p>
  92  * This image demonstrates the example code above for each
  93  * of the three cycle methods:
  94  * <center>
  95  * <img src = "doc-files/LinearGradientPaint.png"
  96  * alt="image showing the output of the example code">
  97  * </center>
  98  *
  99  * @see java.awt.Paint
 100  * @see java.awt.Graphics2D#setPaint
 101  * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
 102  * @since 1.6
 103  */
 104 public final class LinearGradientPaint extends MultipleGradientPaint {
 105 
 106     /** Gradient start and end points. */
 107     private final Point2D start, end;
 108 
 109     /**
 110      * Constructs a {@code LinearGradientPaint} with a default
 111      * {@code NO_CYCLE} repeating method and {@code SRGB} color space.
 112      *
 113      * @param startX the X coordinate of the gradient axis start point
 114      *               in user space
 115      * @param startY the Y coordinate of the gradient axis start point
 116      *               in user space
 117      * @param endX   the X coordinate of the gradient axis end point
 118      *               in user space
 119      * @param endY   the Y coordinate of the gradient axis end point
 120      *               in user space
 121      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
 122      *                  distribution of colors along the gradient
 123      * @param colors array of colors corresponding to each fractional value
 124      *
 125      * @throws NullPointerException
 126      * if {@code fractions} array is null,
 127      * or {@code colors} array is null,
 128      * @throws IllegalArgumentException
 129      * if start and end points are the same points,
 130      * or {@code fractions.length != colors.length},
 131      * or {@code colors} is less than 2 in size,
 132      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
 133      * or the {@code fractions} are not provided in strictly increasing order
 134      */
 135     public LinearGradientPaint(float startX, float startY,
 136                                float endX, float endY,
 137                                float[] fractions, Color[] colors)
 138     {
 139         this(new Point2D.Float(startX, startY),
 140              new Point2D.Float(endX, endY),
 141              fractions,
 142              colors,
 143              CycleMethod.NO_CYCLE);
 144     }
 145 
 146     /**
 147      * Constructs a {@code LinearGradientPaint} with a default {@code SRGB}
 148      * color space.
 149      *
 150      * @param startX the X coordinate of the gradient axis start point
 151      *               in user space
 152      * @param startY the Y coordinate of the gradient axis start point
 153      *               in user space
 154      * @param endX   the X coordinate of the gradient axis end point
 155      *               in user space
 156      * @param endY   the Y coordinate of the gradient axis end point
 157      *               in user space
 158      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
 159      *                  distribution of colors along the gradient
 160      * @param colors array of colors corresponding to each fractional value
 161      * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
 162      *                    or {@code REPEAT}
 163      *
 164      * @throws NullPointerException
 165      * if {@code fractions} array is null,
 166      * or {@code colors} array is null,
 167      * or {@code cycleMethod} is null
 168      * @throws IllegalArgumentException
 169      * if start and end points are the same points,
 170      * or {@code fractions.length != colors.length},
 171      * or {@code colors} is less than 2 in size,
 172      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
 173      * or the {@code fractions} are not provided in strictly increasing order
 174      */
 175     public LinearGradientPaint(float startX, float startY,
 176                                float endX, float endY,
 177                                float[] fractions, Color[] colors,
 178                                CycleMethod cycleMethod)
 179     {
 180         this(new Point2D.Float(startX, startY),
 181              new Point2D.Float(endX, endY),
 182              fractions,
 183              colors,
 184              cycleMethod);
 185     }
 186 
 187     /**
 188      * Constructs a {@code LinearGradientPaint} with a default
 189      * {@code NO_CYCLE} repeating method and {@code SRGB} color space.
 190      *
 191      * @param start the gradient axis start {@code Point2D} in user space
 192      * @param end the gradient axis end {@code Point2D} in user space
 193      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
 194      *                  distribution of colors along the gradient
 195      * @param colors array of colors corresponding to each fractional value
 196      *
 197      * @throws NullPointerException
 198      * if one of the points is null,
 199      * or {@code fractions} array is null,
 200      * or {@code colors} array is null
 201      * @throws IllegalArgumentException
 202      * if start and end points are the same points,
 203      * or {@code fractions.length != colors.length},
 204      * or {@code colors} is less than 2 in size,
 205      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
 206      * or the {@code fractions} are not provided in strictly increasing order
 207      */
 208     public LinearGradientPaint(Point2D start, Point2D end,
 209                                float[] fractions, Color[] colors)
 210     {
 211         this(start, end,
 212              fractions, colors,
 213              CycleMethod.NO_CYCLE);
 214     }
 215 
 216     /**
 217      * Constructs a {@code LinearGradientPaint} with a default {@code SRGB}
 218      * color space.
 219      *
 220      * @param start the gradient axis start {@code Point2D} in user space
 221      * @param end the gradient axis end {@code Point2D} in user space
 222      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
 223      *                  distribution of colors along the gradient
 224      * @param colors array of colors corresponding to each fractional value
 225      * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
 226      *                    or {@code REPEAT}
 227      *
 228      * @throws NullPointerException
 229      * if one of the points is null,
 230      * or {@code fractions} array is null,
 231      * or {@code colors} array is null,
 232      * or {@code cycleMethod} is null
 233      * @throws IllegalArgumentException
 234      * if start and end points are the same points,
 235      * or {@code fractions.length != colors.length},
 236      * or {@code colors} is less than 2 in size,
 237      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
 238      * or the {@code fractions} are not provided in strictly increasing order
 239      */
 240     public LinearGradientPaint(Point2D start, Point2D end,
 241                                float[] fractions, Color[] colors,
 242                                CycleMethod cycleMethod)
 243     {
 244         this(start, end,
 245              fractions, colors,
 246              cycleMethod,
 247              ColorSpaceType.SRGB,
 248              new AffineTransform());
 249     }
 250 
 251     /**
 252      * Constructs a {@code LinearGradientPaint}.
 253      *
 254      * @param start the gradient axis start {@code Point2D} in user space
 255      * @param end the gradient axis end {@code Point2D} in user space
 256      * @param fractions numbers ranging from 0.0 to 1.0 specifying the
 257      *                  distribution of colors along the gradient
 258      * @param colors array of colors corresponding to each fractional value
 259      * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
 260      *                    or {@code REPEAT}
 261      * @param colorSpace which color space to use for interpolation,
 262      *                   either {@code SRGB} or {@code LINEAR_RGB}
 263      * @param gradientTransform transform to apply to the gradient
 264      *
 265      * @throws NullPointerException
 266      * if one of the points is null,
 267      * or {@code fractions} array is null,
 268      * or {@code colors} array is null,
 269      * or {@code cycleMethod} is null,
 270      * or {@code colorSpace} is null,
 271      * or {@code gradientTransform} is null
 272      * @throws IllegalArgumentException
 273      * if start and end points are the same points,
 274      * or {@code fractions.length != colors.length},
 275      * or {@code colors} is less than 2 in size,
 276      * or a {@code fractions} value is less than 0.0 or greater than 1.0,
 277      * or the {@code fractions} are not provided in strictly increasing order
 278      */
 279     @ConstructorProperties({ "startPoint", "endPoint", "fractions", "colors", "cycleMethod", "colorSpace", "transform" })
 280     public LinearGradientPaint(Point2D start, Point2D end,
 281                                float[] fractions, Color[] colors,
 282                                CycleMethod cycleMethod,
 283                                ColorSpaceType colorSpace,
 284                                AffineTransform gradientTransform)
 285     {
 286         super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
 287 
 288         // check input parameters
 289         if (start == null || end == null) {
 290             throw new NullPointerException("Start and end points must be" +
 291                                            "non-null");
 292         }
 293 
 294         if (start.equals(end)) {
 295             throw new IllegalArgumentException("Start point cannot equal" +
 296                                                "endpoint");
 297         }
 298 
 299         // copy the points...
 300         this.start = new Point2D.Double(start.getX(), start.getY());
 301         this.end = new Point2D.Double(end.getX(), end.getY());
 302     }
 303 
 304     /**
 305      * Creates and returns a {@link PaintContext} used to
 306      * generate a linear color gradient pattern.
 307      * See the {@link Paint#createContext specification} of the
 308      * method in the {@link Paint} interface for information
 309      * on null parameter handling.
 310      *
 311      * @param cm the preferred {@link ColorModel} which represents the most convenient
 312      *           format for the caller to receive the pixel data, or {@code null}
 313      *           if there is no preference.
 314      * @param deviceBounds the device space bounding box
 315      *                     of the graphics primitive being rendered.
 316      * @param userBounds the user space bounding box
 317      *                   of the graphics primitive being rendered.
 318      * @param transform the {@link AffineTransform} from user
 319      *              space into device space.
 320      * @param hints the set of hints that the context object can use to
 321      *              choose between rendering alternatives.
 322      * @return the {@code PaintContext} for
 323      *         generating color patterns.
 324      * @see Paint
 325      * @see PaintContext
 326      * @see ColorModel
 327      * @see Rectangle
 328      * @see Rectangle2D
 329      * @see AffineTransform
 330      * @see RenderingHints
 331      */
 332     public PaintContext createContext(ColorModel cm,
 333                                       Rectangle deviceBounds,
 334                                       Rectangle2D userBounds,
 335                                       AffineTransform transform,
 336                                       RenderingHints hints)
 337     {
 338         // avoid modifying the user's transform...
 339         transform = new AffineTransform(transform);
 340         // incorporate the gradient transform
 341         transform.concatenate(gradientTransform);
 342 
 343         if ((fractions.length == 2) &&
 344             (cycleMethod != CycleMethod.REPEAT) &&
 345             (colorSpace == ColorSpaceType.SRGB))
 346         {
 347             // faster to use the basic GradientPaintContext for this
 348             // common case
 349             boolean cyclic = (cycleMethod != CycleMethod.NO_CYCLE);
 350             return new GradientPaintContext(cm, start, end,
 351                                             transform,
 352                                             colors[0], colors[1],
 353                                             cyclic);
 354         } else {
 355             return new LinearGradientPaintContext(this, cm,
 356                                                   deviceBounds, userBounds,
 357                                                   transform, hints,
 358                                                   start, end,
 359                                                   fractions, colors,
 360                                                   cycleMethod, colorSpace);
 361         }
 362     }
 363 
 364     /**
 365      * Returns a copy of the start point of the gradient axis.
 366      *
 367      * @return a {@code Point2D} object that is a copy of the point
 368      * that anchors the first color of this {@code LinearGradientPaint}
 369      */
 370     public Point2D getStartPoint() {
 371         return new Point2D.Double(start.getX(), start.getY());
 372     }
 373 
 374     /**
 375      * Returns a copy of the end point of the gradient axis.
 376      *
 377      * @return a {@code Point2D} object that is a copy of the point
 378      * that anchors the last color of this {@code LinearGradientPaint}
 379      */
 380     public Point2D getEndPoint() {
 381         return new Point2D.Double(end.getX(), end.getY());
 382     }
 383 }