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