1/*
2 * Copyright (c) 2006, 2011, 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
26package java.awt;
27
28import java.awt.geom.AffineTransform;
29import java.awt.image.ColorModel;
30import java.lang.ref.SoftReference;
31import java.util.Arrays;
32
33/**
34 * This is the superclass for Paints which use a multiple color
35 * gradient to fill in their raster.  It provides storage for variables and
36 * enumerated values common to
37 * {@code LinearGradientPaint} and {@code RadialGradientPaint}.
38 *
39 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
40 * @since 1.6
41 */
42public abstract class MultipleGradientPaint implements Paint {
43
44    /** The method to use when painting outside the gradient bounds.
45     * @since 1.6
46     */
47    public static enum CycleMethod {
48        /**
49         * Use the terminal colors to fill the remaining area.
50         */
51        NO_CYCLE,
52
53        /**
54         * Cycle the gradient colors start-to-end, end-to-start
55         * to fill the remaining area.
56         */
57        REFLECT,
58
59        /**
60         * Cycle the gradient colors start-to-end, start-to-end
61         * to fill the remaining area.
62         */
63        REPEAT
64    }
65
66    /** The color space in which to perform the gradient interpolation.
67     * @since 1.6
68     */
69    public static enum ColorSpaceType {
70        /**
71         * Indicates that the color interpolation should occur in sRGB space.
72         */
73        SRGB,
74
75        /**
76         * Indicates that the color interpolation should occur in linearized
77         * RGB space.
78         */
79        LINEAR_RGB
80    }
81
82    /** The transparency of this paint object. */
83    final int transparency;
84
85    /** Gradient keyframe values in the range 0 to 1. */
86    final float[] fractions;
87
88    /** Gradient colors. */
89    final Color[] colors;
90
91    /** Transform to apply to gradient. */
92    final AffineTransform gradientTransform;
93
94    /** The method to use when painting outside the gradient bounds. */
95    final CycleMethod cycleMethod;
96
97    /** The color space in which to perform the gradient interpolation. */
98    final ColorSpaceType colorSpace;
99
100    /**
101     * The following fields are used only by MultipleGradientPaintContext
102     * to cache certain values that remain constant and do not need to be
103     * recalculated for each context created from this paint instance.
104     */
105    ColorModel model;
106    float[] normalizedIntervals;
107    boolean isSimpleLookup;
108    SoftReference<int[][]> gradients;
109    SoftReference<int[]> gradient;
110    int fastGradientArraySize;
111
112    /**
113     * Package-private constructor.
114     *
115     * @param fractions numbers ranging from 0.0 to 1.0 specifying the
116     *                  distribution of colors along the gradient
117     * @param colors array of colors corresponding to each fractional value
118     * @param cycleMethod either {@code NO_CYCLE}, {@code REFLECT},
119     *                    or {@code REPEAT}
120     * @param colorSpace which color space to use for interpolation,
121     *                   either {@code SRGB} or {@code LINEAR_RGB}
122     * @param gradientTransform transform to apply to the gradient
123     *
124     * @throws NullPointerException
125     * if {@code fractions} array is null,
126     * or {@code colors} array is null,
127     * or {@code gradientTransform} is null,
128     * or {@code cycleMethod} is null,
129     * or {@code colorSpace} is null
130     * @throws IllegalArgumentException
131     * if {@code fractions.length != colors.length},
132     * or {@code colors} is less than 2 in size,
133     * or a {@code fractions} value is less than 0.0 or greater than 1.0,
134     * or the {@code fractions} are not provided in strictly increasing order
135     */
136    MultipleGradientPaint(float[] fractions,
137                          Color[] colors,
138                          CycleMethod cycleMethod,
139                          ColorSpaceType colorSpace,
140                          AffineTransform gradientTransform)
141    {
142        if (fractions == null) {
143            throw new NullPointerException("Fractions array cannot be null");
144        }
145
146        if (colors == null) {
147            throw new NullPointerException("Colors array cannot be null");
148        }
149
150        if (cycleMethod == null) {
151            throw new NullPointerException("Cycle method cannot be null");
152        }
153
154        if (colorSpace == null) {
155            throw new NullPointerException("Color space cannot be null");
156        }
157
158        if (gradientTransform == null) {
159            throw new NullPointerException("Gradient transform cannot be "+
160                                           "null");
161        }
162
163        if (fractions.length != colors.length) {
164            throw new IllegalArgumentException("Colors and fractions must " +
165                                               "have equal size");
166        }
167
168        if (colors.length < 2) {
169            throw new IllegalArgumentException("User must specify at least " +
170                                               "2 colors");
171        }
172
173        // check that values are in the proper range and progress
174        // in increasing order from 0 to 1
175        float previousFraction = -1.0f;
176        for (float currentFraction : fractions) {
177            if (currentFraction < 0f || currentFraction > 1f) {
178                throw new IllegalArgumentException("Fraction values must " +
179                                                   "be in the range 0 to 1: " +
180                                                   currentFraction);
181            }
182
183            if (currentFraction <= previousFraction) {
184                throw new IllegalArgumentException("Keyframe fractions " +
185                                                   "must be increasing: " +
186                                                   currentFraction);
187            }
188
189            previousFraction = currentFraction;
190        }
191
192        // We have to deal with the cases where the first gradient stop is not
193        // equal to 0 and/or the last gradient stop is not equal to 1.
194        // In both cases, create a new point and replicate the previous
195        // extreme point's color.
196        boolean fixFirst = false;
197        boolean fixLast = false;
198        int len = fractions.length;
199        int off = 0;
200
201        if (fractions[0] != 0f) {
202            // first stop is not equal to zero, fix this condition
203            fixFirst = true;
204            len++;
205            off++;
206        }
207        if (fractions[fractions.length-1] != 1f) {
208            // last stop is not equal to one, fix this condition
209            fixLast = true;
210            len++;
211        }
212
213        this.fractions = new float[len];
214        System.arraycopy(fractions, 0, this.fractions, off, fractions.length);
215        this.colors = new Color[len];
216        System.arraycopy(colors, 0, this.colors, off, colors.length);
217
218        if (fixFirst) {
219            this.fractions[0] = 0f;
220            this.colors[0] = colors[0];
221        }
222        if (fixLast) {
223            this.fractions[len-1] = 1f;
224            this.colors[len-1] = colors[colors.length - 1];
225        }
226
227        // copy some flags
228        this.colorSpace = colorSpace;
229        this.cycleMethod = cycleMethod;
230
231        // copy the gradient transform
232        this.gradientTransform = new AffineTransform(gradientTransform);
233
234        // determine transparency
235        boolean opaque = true;
236        for (int i = 0; i < colors.length; i++){
237            opaque = opaque && (colors[i].getAlpha() == 0xff);
238        }
239        this.transparency = opaque ? OPAQUE : TRANSLUCENT;
240    }
241
242    /**
243     * Returns a copy of the array of floats used by this gradient
244     * to calculate color distribution.
245     * The returned array always has 0 as its first value and 1 as its
246     * last value, with increasing values in between.
247     *
248     * @return a copy of the array of floats used by this gradient to
249     * calculate color distribution
250     */
251    public final float[] getFractions() {
252        return Arrays.copyOf(fractions, fractions.length);
253    }
254
255    /**
256     * Returns a copy of the array of colors used by this gradient.
257     * The first color maps to the first value in the fractions array,
258     * and the last color maps to the last value in the fractions array.
259     *
260     * @return a copy of the array of colors used by this gradient
261     */
262    public final Color[] getColors() {
263        return Arrays.copyOf(colors, colors.length);
264    }
265
266    /**
267     * Returns the enumerated type which specifies cycling behavior.
268     *
269     * @return the enumerated type which specifies cycling behavior
270     */
271    public final CycleMethod getCycleMethod() {
272        return cycleMethod;
273    }
274
275    /**
276     * Returns the enumerated type which specifies color space for
277     * interpolation.
278     *
279     * @return the enumerated type which specifies color space for
280     * interpolation
281     */
282    public final ColorSpaceType getColorSpace() {
283        return colorSpace;
284    }
285
286    /**
287     * Returns a copy of the transform applied to the gradient.
288     *
289     * <p>
290     * Note that if no transform is applied to the gradient
291     * when it is created, the identity transform is used.
292     *
293     * @return a copy of the transform applied to the gradient
294     */
295    public final AffineTransform getTransform() {
296        return new AffineTransform(gradientTransform);
297    }
298
299    /**
300     * Returns the transparency mode for this {@code Paint} object.
301     *
302     * @return {@code OPAQUE} if all colors used by this
303     *         {@code Paint} object are opaque,
304     *         {@code TRANSLUCENT} if at least one of the
305     *         colors used by this {@code Paint} object is not opaque.
306     * @see java.awt.Transparency
307     */
308    public final int getTransparency() {
309        return transparency;
310    }
311}
312