Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2012, 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.layout;
027
028import com.sun.javafx.scene.layout.region.BorderImageSlices;
029import javafx.geometry.Insets;
030import javafx.scene.image.Image;
031
032/**
033 * Defines properties describing how to render an image as the border of
034 * some Region. A BorderImage must have an Image specified (it cannot be
035 * null). The {@code repeatX} and {@code repeatY} properties define how the
036 * image is to be repeated in each direction. The {@code slices} property
037 * defines how to slice up the image such that it can be stretched across
038 * the Region, while the {@code widths} defines the area on the Region to
039 * fill with the border image. Finally, the {@code outsets} define the distance
040 * outward from the edge of the border over which the border extends. The
041 * outsets of the BorderImage contribute to the outsets of the Border, which
042 * in turn contribute to the bounds of the Region.
043 * <p/>
044 * Because the BorderImage is immutable, it can safely be used in any
045 * cache, and can safely be reused among multiple Regions.
046 * <p/>
047 * When applied to a Region with a defined shape, a BorderImage is not drawn.
048 * TODO Should the insets still be applied?
049 */
050public class BorderImage {
051    /**
052     * The image to be used. This will never be null. If this
053     * image fails to load, then the entire BorderImage will
054     * be skipped at rendering time and will not contribute to
055     * any bounds or other computations.
056     */
057    final Image image;
058    public final Image getImage() { return image; }
059
060    /**
061     * Indicates in what manner (if at all) the border image
062     * is to be repeated along the x-axis of the region. If not specified,
063     * the default value is STRETCH.
064     */
065    final BorderRepeat repeatX;
066    public final BorderRepeat getRepeatX() { return repeatX; }
067
068    /**
069     * Indicates in what manner (if at all) the border image
070     * is to be repeated along the y-axis of the region. If not specified,
071     * the default value is STRETCH.
072     */
073    final BorderRepeat repeatY;
074    public final BorderRepeat getRepeatY() { return repeatY; }
075
076    /**
077     * The widths of the border on each side. These can be defined
078     * as either to be absolute widths or percentages of the size of
079     * the Region, {@see BorderWidths} for more details. If null,
080     * this will default to being 1 pixel wide.
081     */
082    final BorderWidths widths;
083    public final BorderWidths getWidths() { return widths; }
084
085    /**
086     * Defines the slices of the image. JavaFX uses a 4-slice scheme where
087     * the slices each divide up an image into 9 patches. The top-left patch
088     * defines the top-left corner of the border. The top patch defines the top
089     * border and the image making up this patch is stretched horizontally
090     * (or whatever is defined for repeatX) to fill all the required space. The
091     * top-right patch goes in the top-right corner, and the right patch is
092     * stretched vertically (or whatever is defined for repeatY) to fill all the
093     * required space. And so on. The center patch is stretched (or whatever is
094     * defined for repeatX, repeatY) in each dimension. By default the center is
095     * omitted (ie: not drawn), although a BorderImageSlices value of {@code true}
096     * for the {@code filled} property will cause the center to be drawn. A
097     * default value for this property will result in BorderImageSlices.DEFAULT, which
098     * is a border-image-slice of 100% 
099     * @see <a href="http://www.w3.org/TR/css3-background/#the-border-image-slice">border-image-slice</a>
100     */
101    final BorderWidths slices;
102    public final BorderWidths getSlices() { return slices; }
103
104    /**
105     * Specifies whether or not the center patch (as defined by the left, right, top, and bottom slices)
106     * should be drawn.
107     */
108    final boolean filled;
109    public final boolean isFilled() { return filled; }
110
111    /**
112     * The insets of the BorderImage define where the border should be positioned
113     * relative to the edge of the Region. This value will never be null.
114     */
115    final Insets insets;
116    public final Insets getInsets() { return insets; }
117
118    // These two are used by Border to compute the insets and outsets of the border
119    final Insets innerEdge;
120    final Insets outerEdge;
121
122    /**
123     * A cached hash code for faster secondary usage. It is expected
124     * that BorderImage will be pulled from a cache in many cases.
125     */
126    private final int hash;
127
128    /**
129     * Creates a new BorderImage. The image must be specified or a NullPointerException will
130     * be thrown.
131     *
132     * @param image    The image to use. This must not be null.
133     * @param widths    The widths of the border in each dimension. A null value results in Insets.EMPTY.
134     * @param insets    The insets at which to place the border relative to the region.
135     *                  A null value results in Insets.EMPTY.
136     * @param slices    The slices for the image. If null, defaults to BorderImageSlices.DEFAULT
137     * @param repeatX    The repeat value for the border image in the x direction. If null, defaults to STRETCH.
138     * @param repeatY    The repeat value for the border image in the y direction. If null, defaults to the same
139     *                   value as repeatX.
140     */
141    public BorderImage(
142            Image image, BorderWidths widths, Insets insets, BorderWidths slices, boolean filled,
143            BorderRepeat repeatX, BorderRepeat repeatY) {
144        if (image == null) throw new NullPointerException("Image cannot be null");
145        this.image = image;
146        this.widths = widths == null ? BorderWidths.DEFAULT : widths;
147        this.insets = insets == null ? Insets.EMPTY : insets;
148        this.slices = slices == null ? BorderImageSlices.DEFAULT.widths : slices;
149        this.filled = filled;
150        this.repeatX = repeatX == null ? BorderRepeat.STRETCH : repeatX;
151        this.repeatY = repeatY == null ? this.repeatX : repeatY;
152
153        // Compute the inner & outer edge. The outer edge is insets.top,
154        // while the inner edge is insets.top + widths.top
155        outerEdge = new Insets(
156                Math.max(0, -this.insets.getTop()),
157                Math.max(0, -this.insets.getRight()),
158                Math.max(0, -this.insets.getBottom()),
159                Math.max(0, -this.insets.getLeft()));
160        innerEdge = new Insets(
161                this.insets.getTop() + this.widths.getTop(),
162                this.insets.getRight() + this.widths.getRight(),
163                this.insets.getBottom() + this.widths.getBottom(),
164                this.insets.getLeft() + this.widths.getLeft());
165
166        // Pre-compute the hash code. NOTE: all variables are prefixed with "this" so that we
167        // do not accidentally compute the hash based on the constructor arguments rather than
168        // based on the fields themselves!
169        int result = this.image.hashCode();
170        result = 31 * result + this.widths.hashCode();
171        result = 31 * result + this.slices.hashCode();
172        result = 31 * result + this.repeatX.hashCode();
173        result = 31 * result + this.repeatY.hashCode();
174        result = 31 * result + (this.filled ? 1 : 0);
175        hash = result;
176    }
177
178    /**
179     * @inheritDoc
180     */
181    @Override public boolean equals(Object o) {
182        if (this == o) return true;
183        if (o == null || getClass() != o.getClass()) return false;
184        BorderImage that = (BorderImage) o;
185        if (this.hash != that.hash) return false;
186        if (filled != that.filled) return false;
187        if (!image.equals(that.image)) return false;
188        if (repeatX != that.repeatX) return false;
189        if (repeatY != that.repeatY) return false;
190        if (!slices.equals(that.slices)) return false;
191        if (!widths.equals(that.widths)) return false;
192
193        return true;
194    }
195
196    /**
197     * @inheritDoc
198     */
199    @Override public int hashCode() {
200        return hash;
201    }
202}