Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.paint;
027
028import com.sun.javafx.beans.annotations.Default;
029
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Collections;
033import java.util.List;
034
035/**
036 * Defines one element of the ramp of colors to use on a gradient.
037 * For more information see {@code javafx.scene.paint.LinearGradient} and
038 * {@code javafx.scene.paint.RadialGradient}.
039 *
040 * <p>Example:</p>
041 * <pre><code>
042 * // object bounding box relative (proportional:true, default)
043 * Stop[] stops = { new Stop(0, Color.WHITE), new Stop(1, Color.BLACK)};
044 * LinearGradient lg = new LinearGradient(0, 0, 1, 0, true, CycleMethod.No_CYCLE, stops);
045 * Rectangle r = new Rectangle();
046 * r.setFill(lg);
047 *</code></pre>
048 */
049public final class Stop {
050
051    static final List<Stop> NO_STOPS =
052        Collections.unmodifiableList(Arrays.asList(
053            new Stop(0.0, Color.TRANSPARENT),
054            new Stop(1.0, Color.TRANSPARENT)
055        ));
056
057    static List<Stop> normalize(Stop stops[]) {
058        List<Stop> stoplist = (stops == null ? null : Arrays.asList(stops));
059        return normalize(stoplist);
060    }
061
062    static List<Stop> normalize(List<Stop> stops) {
063        if (stops == null) {
064            return NO_STOPS;
065        }
066        Stop zerostop = null;
067        Stop onestop = null;
068        List<Stop> newlist = new ArrayList<Stop>(stops.size());
069        for (Stop s : stops) {
070            if (s == null || s.getColor() == null) continue;
071            double off = s.getOffset();
072            if (off <= 0.0) {
073                if (zerostop == null || off >= zerostop.getOffset()) {
074                    zerostop = s;
075                }
076            } else if (off >= 1.0) {
077                if (onestop == null || off < onestop.getOffset()) {
078                    onestop = s;
079                }
080            } else if (off == off) { // non-NaN
081                for (int i = newlist.size() - 1; i >= 0; i--) {
082                    Stop s2 = newlist.get(i);
083                    if (s2.getOffset() <= off) {
084                        if (s2.getOffset() == off) {
085                            if (i > 0 && newlist.get(i-1).getOffset() == off) {
086                                newlist.set(i, s);
087                            } else {
088                                newlist.add(i+1, s);
089                            }
090                        } else {
091                            newlist.add(i+1, s);
092                        }
093                        s = null;
094                        break;
095                    }
096                }
097                if (s != null) {
098                    newlist.add(0, s);
099                }
100            }
101        }
102
103        if (zerostop == null) {
104            Color zerocolor;
105            if (newlist.isEmpty()) {
106                if (onestop == null) {
107                    return NO_STOPS;
108                }
109                zerocolor = onestop.getColor();
110            } else {
111                zerocolor = newlist.get(0).getColor();
112                if (onestop == null && newlist.size() == 1) {
113                    // Special case for a single color with a non-0,1 offset.
114                    // If we leave the color in there we end up with a 3-color
115                    // gradient with all the colors being identical and we
116                    // will not catch the optimization to a solid color.
117                    newlist.clear();
118                }
119            }
120            zerostop = new Stop(0.0, zerocolor);
121        } else if (zerostop.getOffset() < 0.0) {
122            zerostop = new Stop(0.0, zerostop.getColor());
123        }
124        newlist.add(0, zerostop);
125
126        if (onestop == null) {
127            onestop = new Stop(1.0, newlist.get(newlist.size()-1).getColor());
128        } else if (onestop.getOffset() > 1.0) {
129            onestop = new Stop(1.0, onestop.getColor());
130        }
131        newlist.add(onestop);
132
133        return Collections.unmodifiableList(newlist);
134    }
135
136    /**
137     * The {@code offset} variable is a number ranging from {@code 0} to {@code 1}
138     * that indicates where this gradient stop is placed. For linear gradients,
139     * the {@code offset} variable represents a location along the gradient vector.
140     * For radial gradients, it represents a percentage distance from
141     * the focus point to the edge of the outermost/largest circle.
142     *
143     * @profile common
144     * @defaultValue 0.0
145     */
146    private double offset;
147
148    /**
149     * Gets a number ranging from {@code 0} to {@code 1}
150     * that indicates where this gradient stop is placed. For linear gradients,
151     * the {@code offset} variable represents a location along the gradient vector.
152     * For radial gradients, it represents a percentage distance from
153     * the focus point to the edge of the outermost/largest circle.
154     *
155     * @return position of the Stop within the gradient
156     *         (ranging from {@code 0} to {@code 1})
157     */
158    public final double getOffset() {
159        return offset;
160    }
161
162    /**
163     * The color of the gradient at this offset.
164     *
165     * @profile common
166     * @defaultValue Color.BLACK
167     */
168    private Color color;
169
170    /**
171     * Gets the color of the gradient at this offset.
172     * @return the color of the gradient at this offset
173     */
174    public final Color getColor() {
175        return color;
176    }
177
178    /**
179     * The cached hash code, used to improve performance in situations where
180     * we cache gradients, such as in the CSS routines.
181     */
182    private int hash = 0;
183
184    /**
185     * Creates a new instance of Stop.
186     * @param offset Stop's position (ranging from {@code 0} to {@code 1}
187     * @param color Stop's color
188     */
189    public Stop(double offset, @Default("javafx.scene.paint.Color.BLACK") Color color) {
190        this.offset = offset;
191        this.color = color;
192    }
193
194    /**
195     * Indicates whether some other object is "equal to" this one.
196     * @param obj the reference object with which to compare.
197     * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
198     */
199    @Override public boolean equals(Object obj) {
200        if (obj == null) return false;
201        if (obj == this) return true;
202        if (obj instanceof Stop) {
203            Stop other = (Stop) obj;
204            return offset == other.offset &&
205              (color == null ? other.color == null : color.equals(other.color));
206        } else return false;
207    }
208
209    /**
210     * Returns a hash code for this {@code Stop} object.
211     * @return a hash code for this {@code Stop} object.
212     */ 
213    @Override public int hashCode() {
214        if (hash == 0) {
215            long bits = 17L;
216            bits = 37L * bits + Double.doubleToLongBits(offset);
217            bits = 37L * bits + color.hashCode();
218            hash = (int) (bits ^ (bits >> 32));
219        }
220        return hash;
221    }
222
223    /**
224     * Returns a string representation of this {@code Stop} object.
225     * @return a string representation of this {@code Stop} object.
226     */ 
227    @Override public String toString() {
228        return color + " " + offset*100 + "%";
229    }
230}