1 /* 2 * Copyright (c) 2010, 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 javafx.scene.paint; 27 28 import javafx.beans.NamedArg; 29 import javafx.scene.image.Image; 30 import com.sun.javafx.beans.event.AbstractNotifyListener; 31 import com.sun.javafx.tk.Toolkit; 32 33 /** 34 * <p>The {@code ImagePattern} class fills a shape with an image pattern. The 35 * user may specify the anchor rectangle, which defines the position, 36 * width, and height of the image relative to the upper left corner of the 37 * shape. If the shape extends out of the anchor rectangle, the image is tiled. 38 * </p> 39 * 40 * <p>If the {@code proportional} variable is set to true (the default) 41 * then the anchor rectangle should be specified relative to the unit 42 * square (0.0->1.0) and will be stretched across the shape. 43 * If the {@code proportional} variable is set to false, then the anchor 44 * rectangle should be specified in the local coordinate system of the shape 45 * and the image will be stretched to fit the anchor rectangle. The anchor 46 * rectangle will not be stretched across the shape.</p> 47 * 48 * <p>The example below demonstrates the use of the {@code proportional} 49 * variable. The shapes on the top row use proportional coordinates 50 * (the default) to specify the anchor rectangle. The shapes on the 51 * bottom row use absolute coordinates. The flower image is stretched 52 * to fill the entire triangle shape, while the dot pattern image is tiled 53 * within the circle shape.</p> 54 * 55 <pre><code> 56 import javafx.scene.Scene; 57 import javafx.scene.image.Image; 58 import javafx.scene.paint.ImagePattern; 59 import javafx.scene.shape.Circle; 60 import javafx.scene.shape.Polygon; 61 import javafx.stage.Stage; 62 63 public class HelloImagePattern extends Application { 64 65 private static final String flowerURL = "file:flower.png"; 66 private static final String dotsURL = "file:dots.png"; 67 68 @Override public void start(Stage stage) { 69 stage.setTitle("Image Pattern"); 70 Group root = new Group(); 71 Scene scene = new Scene(root, 600, 450); 72 73 Image dots = new Image(dotsURL); 74 Image flower = new Image(flowerURL); 75 76 Polygon p = new Polygon(); 77 78 p.setLayoutX(10); 79 p.setLayoutY(10); 80 p.getPoints().add(50.0); 81 p.getPoints().add(0.0); 82 p.getPoints().add(100.0); 83 p.getPoints().add(100.0); 84 p.getPoints().add(0.0); 85 p.getPoints().add(100.0); 86 87 p.setFill(new ImagePattern(flower, 0, 0, 1, 1, true)); 88 89 root.getChildren().add(p); 90 91 Polygon p2 = new Polygon(); 92 93 p2.setLayoutX(10); 94 p2.setLayoutY(120); 95 p2.getPoints().add(50.0); 96 p2.getPoints().add(0.0); 97 p2.getPoints().add(100.0); 98 p2.getPoints().add(100.0); 99 p2.getPoints().add(0.0); 100 p2.getPoints().add(100.0); 101 102 p2.setFill(new ImagePattern(flower, 0, 0, 100, 100, false)); 103 104 root.getChildren().add(p2); 105 106 Circle circ = new Circle(50); 107 circ.setTranslateX(120); 108 circ.setTranslateY(10); 109 circ.setCenterX(50); 110 circ.setCenterY(50); 111 circ.setFill(new ImagePattern(dots, 0.2, 0.2, 0.4, 0.4, true)); 112 113 root.getChildren().add(circ); 114 115 Circle circ2 = new Circle(50); 116 circ2.setTranslateX(120); 117 circ2.setTranslateY(10); 118 circ2.setCenterX(50); 119 circ2.setCenterY(50); 120 circ2.setFill(new ImagePattern(dots, 20, 20, 40, 40, false)); 121 122 root.getChildren().add(circ2); 123 stage.setScene(scene); 124 stage.show(); 125 } 126 </pre></code> 127 * <p>The code above produces the following:</p> 128 * <p><img src="doc-files/ImagePattern.png"/></p> 129 * 130 * @since JavaFX 2.2 131 */ 132 public final class ImagePattern extends Paint { 133 134 private Image image; 135 136 /** 137 * Gets the image to be used as a paint. 138 * 139 * @return Image to be used as a paint. 140 */ 141 public final Image getImage() { 142 return image; 143 } 144 145 146 private double x; 147 148 /** 149 * Gets the x origin of the anchor rectangle. 150 * 151 * @defaultValue 0.0 152 * @return The x origin of the anchor rectangle. 153 */ 154 public final double getX() { 155 return x; 156 } 157 158 private double y; 159 160 /** 161 * Gets the y origin of the anchor rectangle. 162 * 163 * @defaultValue 0.0 164 * @return The y origin of the anchor rectangle. 165 */ 166 public final double getY() { 167 return y; 168 } 169 170 171 private double width = 1f; 172 173 /** 174 * Gets the width of the anchor rectangle. 175 * 176 * @defaultValue 1.0 177 * @return The width of the anchor rectangle. 178 */ 179 public final double getWidth() { 180 return width; 181 } 182 183 184 private double height = 1f; 185 186 /** 187 * Gets the height of the anchor rectangle. 188 * 189 * @defaultValue 1.0 190 * @return The height of the anchor rectangle. 191 */ 192 public final double getHeight() { 193 return height; 194 } 195 196 197 private boolean proportional = true; 198 199 /** 200 * Gets a boolean that indicates whether start and end locations are 201 * proportional or absolute. If this flag is true, the two end points are 202 * defined in a coordinate space where coordinates in the range 203 * {@code [0..1]} are scaled to map onto the bounds of the shape that the 204 * pattern fills. If this flag is false, then the coordinates are specified 205 * in the local coordinate system of the node. 206 * 207 * @defaultValue true 208 * @return boolean that is true if this paint is proportional. 209 */ 210 public final boolean isProportional() { 211 return proportional; 212 } 213 214 @Override public final boolean isOpaque() { 215 // We only ever have Prism as our painting system these days, so we can just 216 // cast this platformPaint to a prism paint type and check for isOpaque. 217 // It would be better to change the type on platformPaint accordingly and 218 // ditch the cast. 219 return ((com.sun.prism.paint.ImagePattern)acc_getPlatformPaint()).isOpaque(); 220 } 221 222 private Object platformPaint; 223 224 /** 225 * Creates a new instance of ImagePattern from the specified image. Default 226 * values are used for all other parameters. 227 * 228 * @param image the image to be used as the paint. 229 * @throws NullPointerException if the image is null. 230 * @throws IllegalArgumentException if image is not done loading, 231 * that is if progress is < 1. 232 */ 233 public ImagePattern(@NamedArg("image") Image image) { 234 if (image == null) { 235 throw new NullPointerException("Image must be non-null."); 236 } else if (image.getProgress() < 1.0) { 237 throw new IllegalArgumentException("Image not yet loaded"); 238 } 239 this.image = image; 240 } 241 242 /** 243 * Creates a new instance of ImagePattern. 244 * 245 * @param image the image to be used as the paint. 246 * @param x the x origin of the anchor rectangle. 247 * @param y the y origin of the anchor rectangle. 248 * @param width the width of the anchor rectangle. 249 * @param height the height of the anchor rectangle. 250 * @param proportional whether the coordinates are proportional 251 * to the shape which ImagePattern fills 252 * @throws NullPointerException if the image is null. 253 * @throws IllegalArgumentException if image is not done loading, 254 * that is if progress is < 1. 255 */ 256 public ImagePattern(@NamedArg("image") Image image, @NamedArg("x") double x, @NamedArg("y") double y, @NamedArg("width") double width, 257 @NamedArg("height") double height, @NamedArg("proportional") boolean proportional) { 258 259 if (image == null) { 260 throw new NullPointerException("Image must be non-null."); 261 } else if (image.getProgress() < 1.0) { 262 throw new IllegalArgumentException("Image not yet loaded"); 263 } 264 this.image = image; 265 this.x = x; 266 this.y = y; 267 this.width = width; 268 this.height = height; 269 this.proportional = proportional; 270 } 271 272 @Override 273 boolean acc_isMutable() { 274 return Toolkit.getImageAccessor().isAnimation(image); 275 } 276 277 @Override 278 void acc_addListener(AbstractNotifyListener platformChangeListener) { 279 Toolkit.getImageAccessor().getImageProperty(image) 280 .addListener(platformChangeListener); 281 } 282 283 @Override 284 void acc_removeListener(AbstractNotifyListener platformChangeListener) { 285 Toolkit.getImageAccessor().getImageProperty(image) 286 .removeListener(platformChangeListener); 287 } 288 289 @Override Object acc_getPlatformPaint() { 290 if (acc_isMutable() || platformPaint == null) { 291 platformPaint = Toolkit.getToolkit().getPaint(this); 292 assert platformPaint != null; 293 } 294 return platformPaint; 295 } 296 }