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 
26 package java.awt;
27 
28 import java.awt.geom.AffineTransform;
29 import java.awt.image.ColorModel;
30 import java.lang.ref.SoftReference;
31 import 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  */
42 public 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      */
MultipleGradientPaint(float[] fractions, Color[] colors, CycleMethod cycleMethod, ColorSpaceType colorSpace, AffineTransform gradientTransform)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      */
getFractions()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      */
getColors()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      */
getCycleMethod()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      */
getColorSpace()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      */
getTransform()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      */
getTransparency()308     public final int getTransparency() {
309         return transparency;
310     }
311 }
312