1 /* 2 * Copyright (c) 2008, 2014, 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.image; 27 28 import com.sun.javafx.beans.event.AbstractNotifyListener; 29 import com.sun.javafx.css.StyleManager; 30 import com.sun.javafx.css.converters.URLConverter; 31 import com.sun.javafx.geom.BaseBounds; 32 import com.sun.javafx.geom.transform.BaseTransform; 33 import com.sun.javafx.jmx.MXNodeAlgorithm; 34 import com.sun.javafx.jmx.MXNodeAlgorithmContext; 35 import com.sun.javafx.scene.DirtyBits; 36 import com.sun.javafx.sg.prism.NGImageView; 37 import com.sun.javafx.sg.prism.NGNode; 38 import com.sun.javafx.tk.Toolkit; 39 import javafx.beans.DefaultProperty; 40 import javafx.beans.Observable; 41 import javafx.beans.property.*; 42 import javafx.css.CssMetaData; 43 import javafx.css.Styleable; 44 import javafx.css.StyleableProperty; 45 import javafx.css.StyleableStringProperty; 46 import javafx.geometry.NodeOrientation; 47 import javafx.geometry.Rectangle2D; 48 import javafx.scene.AccessibleRole; 49 import javafx.scene.Node; 50 import java.util.ArrayList; 51 import java.util.Collections; 52 import java.util.List; 53 54 /** 55 * The {@code ImageView} is a {@code Node} used for painting images loaded with 56 * {@link Image} class. 57 * 58 * <p> 59 * This class allows resizing the displayed image (with or without preserving 60 * the original aspect ratio) and specifying a viewport into the source image 61 * for restricting the pixels displayed by this {@code ImageView}. 62 * </p> 63 * 64 * 65 * <p> 66 * Example code for displaying images 67 * </p> 68 * 69 * <pre> 70 * <code> 71 * import javafx.application.Application; 72 * import javafx.geometry.Rectangle2D; 73 * import javafx.scene.Group; 74 * import javafx.scene.Scene; 75 * import javafx.scene.image.Image; 76 * import javafx.scene.image.ImageView; 77 * import javafx.scene.layout.HBox; 78 * import javafx.scene.paint.Color; 79 * import javafx.stage.Stage; 80 * 81 * public class HelloMenu extends Application { 82 * 83 * @Override public void start(Stage stage) { 84 * // load the image 85 * Image image = new Image("flower.png"); 86 * 87 * // simple displays ImageView the image as is 88 * ImageView iv1 = new ImageView(); 89 * iv1.setImage(image); 90 * 91 * // resizes the image to have width of 100 while preserving the ratio and using 92 * // higher quality filtering method; this ImageView is also cached to 93 * // improve performance 94 * ImageView iv2 = new ImageView(); 95 * iv2.setImage(image); 96 * iv2.setFitWidth(100); 97 * iv2.setPreserveRatio(true); 98 * iv2.setSmooth(true); 99 * iv2.setCache(true); 100 * 101 * // defines a viewport into the source image (achieving a "zoom" effect) and 102 * // displays it rotated 103 * ImageView iv3 = new ImageView(); 104 * iv3.setImage(image); 105 * Rectangle2D viewportRect = new Rectangle2D(40, 35, 110, 110); 106 * iv3.setViewport(viewportRect); 107 * iv3.setRotate(90); 108 * 109 * Group root = new Group(); 110 * Scene scene = new Scene(root); 111 * scene.setFill(Color.BLACK); 112 * HBox box = new HBox(); 113 * box.getChildren().add(iv1); 114 * box.getChildren().add(iv2); 115 * box.getChildren().add(iv3); 116 * root.getChildren().add(box); 117 * 118 * stage.setTitle("ImageView"); 119 * stage.setWidth(415); 120 * stage.setHeight(200); 121 * stage.setScene(scene); 122 * stage.sizeToScene(); 123 * stage.show(); 124 * } 125 * 126 * public static void main(String[] args) { 127 * Application.launch(args); 128 * } 129 * } 130 * </code> 131 * </pre> 132 * <p> 133 * The code above produces the following: 134 * </p> 135 * <p> 136 * <img src="doc-files/imageview.png"/> 137 * </p> 138 * @since JavaFX 2.0 139 */ 140 @DefaultProperty("image") 141 public class ImageView extends Node { 142 143 /** 144 * Allocates a new ImageView object. 145 */ 146 public ImageView() { 147 getStyleClass().add(DEFAULT_STYLE_CLASS); 148 setAccessibleRole(AccessibleRole.IMAGE_VIEW); 149 setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); 150 } 151 152 /** 153 * Allocates a new ImageView object with image loaded from the specified 154 * URL. 155 * <p> 156 * The {@code new ImageView(url)} has the same effect as 157 * {@code new ImageView(new Image(url))}. 158 * </p> 159 * 160 * @param url the string representing the URL from which to load the image 161 * @throws NullPointerException if URL is null 162 * @throws IllegalArgumentException if URL is invalid or unsupported 163 * @since JavaFX 2.1 164 */ 165 public ImageView(String url) { 166 this(new Image(url)); 167 } 168 169 /** 170 * Allocates a new ImageView object using the given image. 171 * 172 * @param image Image that this ImageView uses 173 */ 174 public ImageView(Image image) { 175 getStyleClass().add(DEFAULT_STYLE_CLASS); 176 setAccessibleRole(AccessibleRole.IMAGE_VIEW); 177 setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); 178 setImage(image); 179 } 180 181 /** 182 * The {@link Image} to be painted by this {@code ImageView}. 183 * 184 * @defaultValue null 185 */ 186 private ObjectProperty<Image> image; 187 188 public final void setImage(Image value) { 189 imageProperty().set(value); 190 } 191 public final Image getImage() { 192 return image == null ? null : image.get(); 193 } 194 195 private Image oldImage; 196 public final ObjectProperty<Image> imageProperty() { 197 if (image == null) { 198 image = new ObjectPropertyBase<Image>() { 199 200 private boolean needsListeners = false; 201 202 @Override 203 public void invalidated() { 204 Image _image = get(); 205 boolean dimensionChanged = _image == null || oldImage == null || 206 (oldImage.getWidth() != _image.getWidth() || 207 oldImage.getHeight() != _image.getHeight()); 208 209 if (needsListeners) { 210 Toolkit.getImageAccessor().getImageProperty(oldImage). 211 removeListener(platformImageChangeListener.getWeakListener()); 212 } 213 214 needsListeners = _image != null && (_image.isAnimation() || _image.getProgress() < 1); 215 oldImage = _image; 216 217 if (needsListeners) { 218 Toolkit.getImageAccessor().getImageProperty(_image). 219 addListener(platformImageChangeListener.getWeakListener()); 220 } 221 if (dimensionChanged) { 222 invalidateWidthHeight(); 223 impl_geomChanged(); 224 } 225 impl_markDirty(DirtyBits.NODE_CONTENTS); 226 } 227 228 @Override 229 public Object getBean() { 230 return ImageView.this; 231 } 232 233 @Override 234 public String getName() { 235 return "image"; 236 } 237 }; 238 } 239 return image; 240 } 241 242 private StringProperty imageUrl = null; 243 /** 244 * The imageUrl property is set from CSS and then the image property is 245 * set from the invalidated method. This ensures that the same image isn't 246 * reloaded. 247 */ 248 private StringProperty imageUrlProperty() { 249 if (imageUrl == null) { 250 imageUrl = new StyleableStringProperty() { 251 252 @Override 253 protected void invalidated() { 254 255 final String imageUrl = get(); 256 if (imageUrl != null) { 257 setImage(StyleManager.getInstance().getCachedImage(imageUrl)); 258 } else { 259 setImage(null); 260 } 261 } 262 263 @Override 264 public Object getBean() { 265 return ImageView.this; 266 } 267 268 @Override 269 public String getName() { 270 return "imageUrl"; 271 } 272 273 @Override 274 public CssMetaData<ImageView,String> getCssMetaData() { 275 return StyleableProperties.IMAGE; 276 } 277 278 }; 279 } 280 return imageUrl; 281 } 282 283 private final AbstractNotifyListener platformImageChangeListener = 284 new AbstractNotifyListener() { 285 @Override 286 public void invalidated(Observable valueModel) { 287 invalidateWidthHeight(); 288 impl_markDirty(DirtyBits.NODE_CONTENTS); 289 impl_geomChanged(); 290 } 291 }; 292 /** 293 * The current x coordinate of the {@code ImageView} origin. 294 * 295 * @defaultValue 0 296 */ 297 private DoubleProperty x; 298 299 300 public final void setX(double value) { 301 xProperty().set(value); 302 } 303 304 public final double getX() { 305 return x == null ? 0.0 : x.get(); 306 } 307 308 public final DoubleProperty xProperty() { 309 if (x == null) { 310 x = new DoublePropertyBase() { 311 312 @Override 313 protected void invalidated() { 314 impl_markDirty(DirtyBits.NODE_GEOMETRY); 315 impl_geomChanged(); 316 } 317 318 @Override 319 public Object getBean() { 320 return ImageView.this; 321 } 322 323 @Override 324 public String getName() { 325 return "x"; 326 } 327 }; 328 } 329 return x; 330 } 331 332 /** 333 * The current y coordinate of the {@code ImageView} origin. 334 * 335 * @defaultValue 0 336 */ 337 private DoubleProperty y; 338 339 340 public final void setY(double value) { 341 yProperty().set(value); 342 } 343 344 public final double getY() { 345 return y == null ? 0.0 : y.get(); 346 } 347 348 public final DoubleProperty yProperty() { 349 if (y == null) { 350 y = new DoublePropertyBase() { 351 352 @Override 353 protected void invalidated() { 354 impl_markDirty(DirtyBits.NODE_GEOMETRY); 355 impl_geomChanged(); 356 } 357 358 @Override 359 public Object getBean() { 360 return ImageView.this; 361 } 362 363 @Override 364 public String getName() { 365 return "y"; 366 } 367 }; 368 } 369 return y; 370 } 371 372 /** 373 * The width of the bounding box within which the source image is resized as 374 * necessary to fit. If set to a value <= 0, then the intrinsic width of the 375 * image will be used as the {@code fitWidth}. 376 * <p/> 377 * See {@link #preserveRatio} for information on interaction between image 378 * view's {@code fitWidth}, {@code fitHeight} and {@code preserveRatio} 379 * attributes. 380 * 381 * @defaultValue 0 382 */ 383 private DoubleProperty fitWidth; 384 385 386 public final void setFitWidth(double value) { 387 fitWidthProperty().set(value); 388 } 389 390 public final double getFitWidth() { 391 return fitWidth == null ? 0.0 : fitWidth.get(); 392 } 393 394 public final DoubleProperty fitWidthProperty() { 395 if (fitWidth == null) { 396 fitWidth = new DoublePropertyBase() { 397 398 @Override 399 protected void invalidated() { 400 invalidateWidthHeight(); 401 impl_markDirty(DirtyBits.NODE_VIEWPORT); 402 impl_geomChanged(); 403 } 404 405 @Override 406 public Object getBean() { 407 return ImageView.this; 408 } 409 410 @Override 411 public String getName() { 412 return "fitWidth"; 413 } 414 }; 415 } 416 return fitWidth; 417 } 418 419 /** 420 * The height of the bounding box within which the source image is resized 421 * as necessary to fit. If set to a value <= 0, then the intrinsic height of 422 * the image will be used as the {@code fitHeight}. 423 * <p> 424 * See {@link #preserveRatio} for information on interaction between image 425 * view's {@code fitWidth}, {@code fitHeight} and {@code preserveRatio} 426 * attributes. 427 * </p> 428 * 429 * @defaultValue 0 430 */ 431 private DoubleProperty fitHeight; 432 433 434 public final void setFitHeight(double value) { 435 fitHeightProperty().set(value); 436 } 437 438 public final double getFitHeight() { 439 return fitHeight == null ? 0.0 : fitHeight.get(); 440 } 441 442 public final DoubleProperty fitHeightProperty() { 443 if (fitHeight == null) { 444 fitHeight = new DoublePropertyBase() { 445 446 @Override 447 protected void invalidated() { 448 invalidateWidthHeight(); 449 impl_markDirty(DirtyBits.NODE_VIEWPORT); 450 impl_geomChanged(); 451 } 452 453 @Override 454 public Object getBean() { 455 return ImageView.this; 456 } 457 458 @Override 459 public String getName() { 460 return "fitHeight"; 461 } 462 }; 463 } 464 return fitHeight; 465 } 466 467 /** 468 * Indicates whether to preserve the aspect ratio of the source image when 469 * scaling to fit the image within the fitting bounding box. 470 * <p/> 471 * If set to {@code true}, it affects the dimensions of this 472 * {@code ImageView} in the following way * 473 * <ul> 474 * <li>If only {@code fitWidth} is set, height is scaled to preserve ratio 475 * <li>If only {@code fitHeight} is set, width is scaled to preserve ratio 476 * <li>If both are set, they both may be scaled to get the best fit in a 477 * width by height rectangle while preserving the original aspect ratio 478 * </ul> 479 * 480 * If unset or set to {@code false}, it affects the dimensions of this 481 * {@code ImageView} in the following way * 482 * <ul> 483 * <li>If only {@code fitWidth} is set, image's view width is scaled to 484 * match and height is unchanged; 485 * <li>If only {@code fitHeight} is set, image's view height is scaled to 486 * match and height is unchanged; 487 * <li>If both are set, the image view is scaled to match both. 488 * </ul> 489 * </p> 490 * Note that the dimensions of this node as reported by the node's bounds 491 * will be equal to the size of the scaled image and is guaranteed to be 492 * contained within {@code fitWidth x fitHeight} bonding box. 493 * 494 * @defaultValue false 495 */ 496 private BooleanProperty preserveRatio; 497 498 499 public final void setPreserveRatio(boolean value) { 500 preserveRatioProperty().set(value); 501 } 502 503 public final boolean isPreserveRatio() { 504 return preserveRatio == null ? false : preserveRatio.get(); 505 } 506 507 public final BooleanProperty preserveRatioProperty() { 508 if (preserveRatio == null) { 509 preserveRatio = new BooleanPropertyBase() { 510 511 @Override 512 protected void invalidated() { 513 invalidateWidthHeight(); 514 impl_markDirty(DirtyBits.NODE_VIEWPORT); 515 impl_geomChanged(); 516 } 517 518 @Override 519 public Object getBean() { 520 return ImageView.this; 521 } 522 523 @Override 524 public String getName() { 525 return "preserveRatio"; 526 } 527 }; 528 } 529 return preserveRatio; 530 } 531 532 /** 533 * Indicates whether to use a better quality filtering algorithm or a faster 534 * one when transforming or scaling the source image to fit within the 535 * bounding box provided by {@code fitWidth} and {@code fitHeight}. 536 * 537 * <p> 538 * If set to {@code true} a better quality filtering will be used, if set to 539 * {@code false} a faster but lesser quality filtering will be used. 540 * </p> 541 * 542 * <p> 543 * The default value depends on platform configuration. 544 * </p> 545 * 546 * @defaultValue platform-dependent 547 */ 548 private BooleanProperty smooth; 549 550 551 public final void setSmooth(boolean value) { 552 smoothProperty().set(value); 553 } 554 555 public final boolean isSmooth() { 556 return smooth == null ? SMOOTH_DEFAULT : smooth.get(); 557 } 558 559 public final BooleanProperty smoothProperty() { 560 if (smooth == null) { 561 smooth = new BooleanPropertyBase(SMOOTH_DEFAULT) { 562 563 @Override 564 protected void invalidated() { 565 impl_markDirty(DirtyBits.NODE_SMOOTH); 566 } 567 568 @Override 569 public Object getBean() { 570 return ImageView.this; 571 } 572 573 @Override 574 public String getName() { 575 return "smooth"; 576 } 577 }; 578 } 579 return smooth; 580 } 581 582 /** 583 * Platform-dependent default value of the {@link #smoothProperty smooth} property. 584 */ 585 public static final boolean SMOOTH_DEFAULT = Toolkit.getToolkit() 586 .getDefaultImageSmooth(); 587 /** 588 * The rectangular viewport into the image. The viewport is specified in the 589 * coordinates of the image, prior to scaling or any other transformations. 590 * 591 * <p> 592 * If {@code viewport} is {@code null}, the entire image is displayed. If 593 * {@code viewport} is non-{@code null}, only the portion of the image which 594 * falls within the viewport will be displayed. If the image does not fully 595 * cover the viewport then any remaining area of the viewport will be empty. 596 * </p> 597 * 598 * @defaultValue null 599 */ 600 private ObjectProperty<Rectangle2D> viewport; 601 602 603 public final void setViewport(Rectangle2D value) { 604 viewportProperty().set(value); 605 } 606 607 public final Rectangle2D getViewport() { 608 return viewport == null ? null : viewport.get(); 609 } 610 611 public final ObjectProperty<Rectangle2D> viewportProperty() { 612 if (viewport == null) { 613 viewport = new ObjectPropertyBase<Rectangle2D>() { 614 615 @Override 616 protected void invalidated() { 617 invalidateWidthHeight(); 618 impl_markDirty(DirtyBits.NODE_VIEWPORT); 619 impl_geomChanged(); 620 } 621 622 @Override 623 public Object getBean() { 624 return ImageView.this; 625 } 626 627 @Override 628 public String getName() { 629 return "viewport"; 630 } 631 }; 632 } 633 return viewport; 634 } 635 636 // Need to track changes to image width and image height and recompute 637 // bounds when changed. 638 // imageWidth = bind image.width on replace { 639 // impl_geomChanged(); 640 // } 641 // 642 // imageHeight = bind image.height on replace { 643 // impl_geomChanged(); 644 // } 645 646 private double destWidth, destHeight; 647 648 /** 649 * @treatAsPrivate implementation detail 650 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 651 */ 652 @Deprecated 653 @Override protected NGNode impl_createPeer() { 654 return new NGImageView(); 655 } 656 657 /** 658 * @treatAsPrivate implementation detail 659 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 660 */ 661 @Deprecated 662 @Override public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 663 recomputeWidthHeight(); 664 665 bounds = bounds.deriveWithNewBounds((float)getX(), (float)getY(), 0.0f, 666 (float)(getX() + destWidth), (float)(getY() + destHeight), 0.0f); 667 bounds = tx.transform(bounds, bounds); 668 return bounds; 669 } 670 671 private boolean validWH; 672 673 private void invalidateWidthHeight() { 674 validWH = false; 675 } 676 677 private void recomputeWidthHeight() { 678 if (validWH) { 679 return; 680 } 681 Image localImage = getImage(); 682 Rectangle2D localViewport = getViewport(); 683 684 double w = 0; 685 double h = 0; 686 if (localViewport != null && localViewport.getWidth() > 0 && localViewport.getHeight() > 0) { 687 w = localViewport.getWidth(); 688 h = localViewport.getHeight(); 689 } else if (localImage != null) { 690 w = localImage.getWidth(); 691 h = localImage.getHeight(); 692 } 693 694 double localFitWidth = getFitWidth(); 695 double localFitHeight = getFitHeight(); 696 697 if (isPreserveRatio() && w > 0 && h > 0 && (localFitWidth > 0 || localFitHeight > 0)) { 698 if (localFitWidth <= 0 || (localFitHeight > 0 && localFitWidth * h > localFitHeight * w)) { 699 w = w * localFitHeight / h; 700 h = localFitHeight; 701 } else { 702 h = h * localFitWidth / w; 703 w = localFitWidth; 704 } 705 } else { 706 if (localFitWidth > 0f) { 707 w = localFitWidth; 708 } 709 if (localFitHeight > 0f) { 710 h = localFitHeight; 711 } 712 } 713 714 // Store these values for use later in impl_computeContains() to support 715 // Node.contains(). 716 destWidth = w; 717 destHeight = h; 718 719 validWH = true; 720 } 721 722 /** 723 * @treatAsPrivate implementation detail 724 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 725 */ 726 @Deprecated 727 @Override protected boolean impl_computeContains(double localX, double localY) { 728 if (getImage() == null) { 729 return false; 730 } 731 732 recomputeWidthHeight(); 733 // Local Note bounds contain test is already done by the caller. 734 // (Node.contains()). 735 736 double dx = localX - getX(); 737 double dy = localY - getY(); 738 739 Image localImage = getImage(); 740 double srcWidth = localImage.getWidth(); 741 double srcHeight = localImage.getHeight(); 742 double viewWidth = srcWidth; 743 double viewHeight = srcHeight; 744 double vw = 0; 745 double vh = 0; 746 double vminx = 0; 747 double vminy = 0; 748 Rectangle2D localViewport = getViewport(); 749 if (localViewport != null) { 750 vw = localViewport.getWidth(); 751 vh = localViewport.getHeight(); 752 vminx = localViewport.getMinX(); 753 vminy = localViewport.getMinY(); 754 } 755 756 if (vw > 0 && vh > 0) { 757 viewWidth = vw; 758 viewHeight = vh; 759 } 760 761 // desWidth Note and destHeight are computed by impl_computeGeomBounds() 762 // via a call from Node.contains() before calling 763 // impl_computeContains(). 764 // Transform into image's coordinate system. 765 dx = vminx + dx * viewWidth / destWidth; 766 dy = vminy + dy * viewHeight / destHeight; 767 // test whether it's inside the original image AND inside of viewport 768 // (viewport may stick out from the image bounds) 769 if (dx < 0.0 || dy < 0.0 || dx >= srcWidth || dy >= srcHeight || 770 dx < vminx || dy < vminy || 771 dx >= vminx + viewWidth || dy >= vminy + viewHeight) { 772 return false; 773 } 774 // Do alpha test on the picked pixel. 775 return Toolkit.getToolkit().imageContains(localImage.impl_getPlatformImage(), (float)dx, (float)dy); 776 } 777 778 /*************************************************************************** 779 * * Stylesheet Handling * * 780 **************************************************************************/ 781 782 private static final String DEFAULT_STYLE_CLASS = "image-view"; 783 784 /** 785 * Super-lazy instantiation pattern from Bill Pugh. 786 * @treatAsPrivate implementation detail 787 */ 788 private static class StyleableProperties { 789 // TODO 790 // "preserve-ratio","smooth","viewport","fit-width","fit-height" 791 private static final CssMetaData<ImageView, String> IMAGE = 792 new CssMetaData<ImageView,String>("-fx-image", 793 URLConverter.getInstance()) { 794 795 @Override 796 public boolean isSettable(ImageView n) { 797 // Note that we care about the image, not imageUrl 798 return n.image == null || !n.image.isBound(); 799 } 800 801 @Override 802 public StyleableProperty<String> getStyleableProperty(ImageView n) { 803 return (StyleableProperty<String>)n.imageUrlProperty(); 804 } 805 }; 806 807 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 808 static { 809 final List<CssMetaData<? extends Styleable, ?>> styleables = 810 new ArrayList<CssMetaData<? extends Styleable, ?>>(Node.getClassCssMetaData()); 811 styleables.add(IMAGE); 812 STYLEABLES = Collections.unmodifiableList(styleables); 813 } 814 } 815 816 /** 817 * @return The CssMetaData associated with this class, which may include the 818 * CssMetaData of its super classes. 819 * @since JavaFX 8.0 820 */ 821 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 822 return StyleableProperties.STYLEABLES; 823 } 824 825 /** 826 * {@inheritDoc} 827 * 828 * @since JavaFX 8.0 829 */ 830 831 832 @Override 833 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 834 return getClassCssMetaData(); 835 } 836 837 void updateViewport() { 838 recomputeWidthHeight(); 839 if (getImage() == null || getImage().impl_getPlatformImage() == null) { 840 return; 841 } 842 843 Rectangle2D localViewport = getViewport(); 844 final NGImageView peer = impl_getPeer(); 845 if (localViewport != null) { 846 peer.setViewport((float)localViewport.getMinX(), (float)localViewport.getMinY(), 847 (float)localViewport.getWidth(), (float)localViewport.getHeight(), 848 (float)destWidth, (float)destHeight); 849 } else { 850 peer.setViewport(0, 0, 0, 0, (float)destWidth, (float)destHeight); 851 } 852 } 853 854 /** 855 * @treatAsPrivate implementation detail 856 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 857 */ 858 @Deprecated 859 @Override public void impl_updatePeer() { 860 super.impl_updatePeer(); 861 862 final NGImageView peer = impl_getPeer(); 863 if (impl_isDirty(DirtyBits.NODE_GEOMETRY)) { 864 peer.setX((float)getX()); 865 peer.setY((float)getY()); 866 } 867 if (impl_isDirty(DirtyBits.NODE_SMOOTH)) { 868 peer.setSmooth(isSmooth()); 869 } 870 if (impl_isDirty(DirtyBits.NODE_CONTENTS)) { 871 peer.setImage(getImage()!= null? getImage().impl_getPlatformImage():null); 872 } 873 // The NG part expects this to be called when image changes 874 if (impl_isDirty(DirtyBits.NODE_VIEWPORT) || impl_isDirty(DirtyBits.NODE_CONTENTS)) { 875 updateViewport(); 876 } 877 } 878 879 /** 880 * @treatAsPrivate implementation detail 881 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 882 */ 883 @Deprecated 884 @Override public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) { 885 return alg.processLeafNode(this, ctx); 886 } 887 }