138 * <img src="doc-files/imageview.png"/>
139 * </p>
140 * @since JavaFX 2.0
141 */
142 @DefaultProperty("image")
143 public class ImageView extends Node {
144 static {
145 // This is used by classes in different packages to get access to
146 // private and package private methods.
147 ImageViewHelper.setImageViewAccessor(new ImageViewHelper.ImageViewAccessor() {
148 @Override
149 public NGNode doCreatePeer(Node node) {
150 return ((ImageView) node).doCreatePeer();
151 }
152
153 @Override
154 public void doUpdatePeer(Node node) {
155 ((ImageView) node).doUpdatePeer();
156 }
157
158 });
159 }
160
161 {
162 // To initialize the class helper at the begining each constructor of this class
163 ImageViewHelper.initHelper(this);
164 }
165 /**
166 * Allocates a new ImageView object.
167 */
168 public ImageView() {
169 getStyleClass().add(DEFAULT_STYLE_CLASS);
170 setAccessibleRole(AccessibleRole.IMAGE_VIEW);
171 setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
172 }
173
174 /**
175 * Allocates a new ImageView object with image loaded from the specified
176 * URL.
177 * <p>
225 public void invalidated() {
226 Image _image = get();
227 boolean dimensionChanged = _image == null || oldImage == null ||
228 (oldImage.getWidth() != _image.getWidth() ||
229 oldImage.getHeight() != _image.getHeight());
230
231 if (needsListeners) {
232 Toolkit.getImageAccessor().getImageProperty(oldImage).
233 removeListener(platformImageChangeListener.getWeakListener());
234 }
235
236 needsListeners = _image != null && (_image.isAnimation() || _image.getProgress() < 1);
237 oldImage = _image;
238
239 if (needsListeners) {
240 Toolkit.getImageAccessor().getImageProperty(_image).
241 addListener(platformImageChangeListener.getWeakListener());
242 }
243 if (dimensionChanged) {
244 invalidateWidthHeight();
245 impl_geomChanged();
246 }
247 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_CONTENTS);
248 }
249
250 @Override
251 public Object getBean() {
252 return ImageView.this;
253 }
254
255 @Override
256 public String getName() {
257 return "image";
258 }
259 };
260 }
261 return image;
262 }
263
264 private StringProperty imageUrl = null;
265 /**
291 public String getName() {
292 return "imageUrl";
293 }
294
295 @Override
296 public CssMetaData<ImageView,String> getCssMetaData() {
297 return StyleableProperties.IMAGE;
298 }
299
300 };
301 }
302 return imageUrl;
303 }
304
305 private final AbstractNotifyListener platformImageChangeListener =
306 new AbstractNotifyListener() {
307 @Override
308 public void invalidated(Observable valueModel) {
309 invalidateWidthHeight();
310 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_CONTENTS);
311 impl_geomChanged();
312 }
313 };
314 /**
315 * The current x coordinate of the {@code ImageView} origin.
316 *
317 * @defaultValue 0
318 */
319 private DoubleProperty x;
320
321
322 public final void setX(double value) {
323 xProperty().set(value);
324 }
325
326 public final double getX() {
327 return x == null ? 0.0 : x.get();
328 }
329
330 public final DoubleProperty xProperty() {
331 if (x == null) {
332 x = new DoublePropertyBase() {
333
334 @Override
335 protected void invalidated() {
336 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_GEOMETRY);
337 impl_geomChanged();
338 }
339
340 @Override
341 public Object getBean() {
342 return ImageView.this;
343 }
344
345 @Override
346 public String getName() {
347 return "x";
348 }
349 };
350 }
351 return x;
352 }
353
354 /**
355 * The current y coordinate of the {@code ImageView} origin.
356 *
357 * @defaultValue 0
358 */
359 private DoubleProperty y;
360
361
362 public final void setY(double value) {
363 yProperty().set(value);
364 }
365
366 public final double getY() {
367 return y == null ? 0.0 : y.get();
368 }
369
370 public final DoubleProperty yProperty() {
371 if (y == null) {
372 y = new DoublePropertyBase() {
373
374 @Override
375 protected void invalidated() {
376 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_GEOMETRY);
377 impl_geomChanged();
378 }
379
380 @Override
381 public Object getBean() {
382 return ImageView.this;
383 }
384
385 @Override
386 public String getName() {
387 return "y";
388 }
389 };
390 }
391 return y;
392 }
393
394 /**
395 * The width of the bounding box within which the source image is resized as
396 * necessary to fit. If set to a value <= 0, then the intrinsic width of the
397 * image will be used as the {@code fitWidth}.
404 */
405 private DoubleProperty fitWidth;
406
407
408 public final void setFitWidth(double value) {
409 fitWidthProperty().set(value);
410 }
411
412 public final double getFitWidth() {
413 return fitWidth == null ? 0.0 : fitWidth.get();
414 }
415
416 public final DoubleProperty fitWidthProperty() {
417 if (fitWidth == null) {
418 fitWidth = new DoublePropertyBase() {
419
420 @Override
421 protected void invalidated() {
422 invalidateWidthHeight();
423 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
424 impl_geomChanged();
425 }
426
427 @Override
428 public Object getBean() {
429 return ImageView.this;
430 }
431
432 @Override
433 public String getName() {
434 return "fitWidth";
435 }
436 };
437 }
438 return fitWidth;
439 }
440
441 /**
442 * The height of the bounding box within which the source image is resized
443 * as necessary to fit. If set to a value <= 0, then the intrinsic height of
444 * the image will be used as the {@code fitHeight}.
452 */
453 private DoubleProperty fitHeight;
454
455
456 public final void setFitHeight(double value) {
457 fitHeightProperty().set(value);
458 }
459
460 public final double getFitHeight() {
461 return fitHeight == null ? 0.0 : fitHeight.get();
462 }
463
464 public final DoubleProperty fitHeightProperty() {
465 if (fitHeight == null) {
466 fitHeight = new DoublePropertyBase() {
467
468 @Override
469 protected void invalidated() {
470 invalidateWidthHeight();
471 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
472 impl_geomChanged();
473 }
474
475 @Override
476 public Object getBean() {
477 return ImageView.this;
478 }
479
480 @Override
481 public String getName() {
482 return "fitHeight";
483 }
484 };
485 }
486 return fitHeight;
487 }
488
489 /**
490 * Indicates whether to preserve the aspect ratio of the source image when
491 * scaling to fit the image within the fitting bounding box.
492 * <p/>
517 */
518 private BooleanProperty preserveRatio;
519
520
521 public final void setPreserveRatio(boolean value) {
522 preserveRatioProperty().set(value);
523 }
524
525 public final boolean isPreserveRatio() {
526 return preserveRatio == null ? false : preserveRatio.get();
527 }
528
529 public final BooleanProperty preserveRatioProperty() {
530 if (preserveRatio == null) {
531 preserveRatio = new BooleanPropertyBase() {
532
533 @Override
534 protected void invalidated() {
535 invalidateWidthHeight();
536 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
537 impl_geomChanged();
538 }
539
540 @Override
541 public Object getBean() {
542 return ImageView.this;
543 }
544
545 @Override
546 public String getName() {
547 return "preserveRatio";
548 }
549 };
550 }
551 return preserveRatio;
552 }
553
554 /**
555 * Indicates whether to use a better quality filtering algorithm or a faster
556 * one when transforming or scaling the source image to fit within the
557 * bounding box provided by {@code fitWidth} and {@code fitHeight}.
621 */
622 private ObjectProperty<Rectangle2D> viewport;
623
624
625 public final void setViewport(Rectangle2D value) {
626 viewportProperty().set(value);
627 }
628
629 public final Rectangle2D getViewport() {
630 return viewport == null ? null : viewport.get();
631 }
632
633 public final ObjectProperty<Rectangle2D> viewportProperty() {
634 if (viewport == null) {
635 viewport = new ObjectPropertyBase<Rectangle2D>() {
636
637 @Override
638 protected void invalidated() {
639 invalidateWidthHeight();
640 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
641 impl_geomChanged();
642 }
643
644 @Override
645 public Object getBean() {
646 return ImageView.this;
647 }
648
649 @Override
650 public String getName() {
651 return "viewport";
652 }
653 };
654 }
655 return viewport;
656 }
657
658 // Need to track changes to image width and image height and recompute
659 // bounds when changed.
660 // imageWidth = bind image.width on replace {
661 // impl_geomChanged();
662 // }
663 //
664 // imageHeight = bind image.height on replace {
665 // impl_geomChanged();
666 // }
667
668 private double destWidth, destHeight;
669
670 /*
671 * Note: This method MUST only be called via its accessor method.
672 */
673 private NGNode doCreatePeer() {
674 return new NGImageView();
675 }
676
677 /**
678 * @treatAsPrivate implementation detail
679 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
680 */
681 @Deprecated
682 @Override public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
683 recomputeWidthHeight();
684
685 bounds = bounds.deriveWithNewBounds((float)getX(), (float)getY(), 0.0f,
686 (float)(getX() + destWidth), (float)(getY() + destHeight), 0.0f);
687 bounds = tx.transform(bounds, bounds);
688 return bounds;
689 }
690
691 private boolean validWH;
692
693 private void invalidateWidthHeight() {
694 validWH = false;
695 }
696
697 private void recomputeWidthHeight() {
698 if (validWH) {
699 return;
700 }
701 Image localImage = getImage();
702 Rectangle2D localViewport = getViewport();
714 double localFitWidth = getFitWidth();
715 double localFitHeight = getFitHeight();
716
717 if (isPreserveRatio() && w > 0 && h > 0 && (localFitWidth > 0 || localFitHeight > 0)) {
718 if (localFitWidth <= 0 || (localFitHeight > 0 && localFitWidth * h > localFitHeight * w)) {
719 w = w * localFitHeight / h;
720 h = localFitHeight;
721 } else {
722 h = h * localFitWidth / w;
723 w = localFitWidth;
724 }
725 } else {
726 if (localFitWidth > 0f) {
727 w = localFitWidth;
728 }
729 if (localFitHeight > 0f) {
730 h = localFitHeight;
731 }
732 }
733
734 // Store these values for use later in impl_computeContains() to support
735 // Node.contains().
736 destWidth = w;
737 destHeight = h;
738
739 validWH = true;
740 }
741
742 /**
743 * @treatAsPrivate implementation detail
744 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
745 */
746 @Deprecated
747 @Override protected boolean impl_computeContains(double localX, double localY) {
748 if (getImage() == null) {
749 return false;
750 }
751
752 recomputeWidthHeight();
753 // Local Note bounds contain test is already done by the caller.
754 // (Node.contains()).
755
756 double dx = localX - getX();
757 double dy = localY - getY();
758
759 Image localImage = getImage();
760 double srcWidth = localImage.getWidth();
761 double srcHeight = localImage.getHeight();
762 double viewWidth = srcWidth;
763 double viewHeight = srcHeight;
764 double vw = 0;
765 double vh = 0;
766 double vminx = 0;
767 double vminy = 0;
768 Rectangle2D localViewport = getViewport();
769 if (localViewport != null) {
770 vw = localViewport.getWidth();
771 vh = localViewport.getHeight();
772 vminx = localViewport.getMinX();
773 vminy = localViewport.getMinY();
774 }
775
776 if (vw > 0 && vh > 0) {
777 viewWidth = vw;
778 viewHeight = vh;
779 }
780
781 // desWidth Note and destHeight are computed by impl_computeGeomBounds()
782 // via a call from Node.contains() before calling
783 // impl_computeContains().
784 // Transform into image's coordinate system.
785 dx = vminx + dx * viewWidth / destWidth;
786 dy = vminy + dy * viewHeight / destHeight;
787 // test whether it's inside the original image AND inside of viewport
788 // (viewport may stick out from the image bounds)
789 if (dx < 0.0 || dy < 0.0 || dx >= srcWidth || dy >= srcHeight ||
790 dx < vminx || dy < vminy ||
791 dx >= vminx + viewWidth || dy >= vminy + viewHeight) {
792 return false;
793 }
794 // Do alpha test on the picked pixel.
795 return Toolkit.getToolkit().imageContains(
796 Toolkit.getImageAccessor().getPlatformImage(localImage), (float)dx, (float)dy);
797 }
798
799 /***************************************************************************
800 * * Stylesheet Handling * *
801 **************************************************************************/
802
803 private static final String DEFAULT_STYLE_CLASS = "image-view";
876 */
877 private void doUpdatePeer() {
878 final NGImageView peer = NodeHelper.getPeer(this);
879 if (NodeHelper.isDirty(this, DirtyBits.NODE_GEOMETRY)) {
880 peer.setX((float)getX());
881 peer.setY((float)getY());
882 }
883 if (NodeHelper.isDirty(this, DirtyBits.NODE_SMOOTH)) {
884 peer.setSmooth(isSmooth());
885 }
886 if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
887 peer.setImage(getImage() != null
888 ? Toolkit.getImageAccessor().getPlatformImage(getImage()) : null);
889 }
890 // The NG part expects this to be called when image changes
891 if (NodeHelper.isDirty(this, DirtyBits.NODE_VIEWPORT) || NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
892 updateViewport();
893 }
894 }
895
896 /**
897 * @treatAsPrivate implementation detail
898 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
899 */
900 @Deprecated
901 @Override public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
902 return alg.processLeafNode(this, ctx);
903 }
904 }
|
138 * <img src="doc-files/imageview.png"/>
139 * </p>
140 * @since JavaFX 2.0
141 */
142 @DefaultProperty("image")
143 public class ImageView extends Node {
144 static {
145 // This is used by classes in different packages to get access to
146 // private and package private methods.
147 ImageViewHelper.setImageViewAccessor(new ImageViewHelper.ImageViewAccessor() {
148 @Override
149 public NGNode doCreatePeer(Node node) {
150 return ((ImageView) node).doCreatePeer();
151 }
152
153 @Override
154 public void doUpdatePeer(Node node) {
155 ((ImageView) node).doUpdatePeer();
156 }
157
158 @Override
159 public BaseBounds doComputeGeomBounds(Node node,
160 BaseBounds bounds, BaseTransform tx) {
161 return ((ImageView) node).doComputeGeomBounds(bounds, tx);
162 }
163
164 @Override
165 public boolean doComputeContains(Node node, double localX, double localY) {
166 return ((ImageView) node).doComputeContains(localX, localY);
167 }
168
169 @Override
170 public Object doProcessMXNode(Node node, MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
171 return ((ImageView) node).doProcessMXNode(alg, ctx);
172 }
173 });
174 }
175
176 {
177 // To initialize the class helper at the begining each constructor of this class
178 ImageViewHelper.initHelper(this);
179 }
180 /**
181 * Allocates a new ImageView object.
182 */
183 public ImageView() {
184 getStyleClass().add(DEFAULT_STYLE_CLASS);
185 setAccessibleRole(AccessibleRole.IMAGE_VIEW);
186 setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT);
187 }
188
189 /**
190 * Allocates a new ImageView object with image loaded from the specified
191 * URL.
192 * <p>
240 public void invalidated() {
241 Image _image = get();
242 boolean dimensionChanged = _image == null || oldImage == null ||
243 (oldImage.getWidth() != _image.getWidth() ||
244 oldImage.getHeight() != _image.getHeight());
245
246 if (needsListeners) {
247 Toolkit.getImageAccessor().getImageProperty(oldImage).
248 removeListener(platformImageChangeListener.getWeakListener());
249 }
250
251 needsListeners = _image != null && (_image.isAnimation() || _image.getProgress() < 1);
252 oldImage = _image;
253
254 if (needsListeners) {
255 Toolkit.getImageAccessor().getImageProperty(_image).
256 addListener(platformImageChangeListener.getWeakListener());
257 }
258 if (dimensionChanged) {
259 invalidateWidthHeight();
260 NodeHelper.geomChanged(ImageView.this);
261 }
262 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_CONTENTS);
263 }
264
265 @Override
266 public Object getBean() {
267 return ImageView.this;
268 }
269
270 @Override
271 public String getName() {
272 return "image";
273 }
274 };
275 }
276 return image;
277 }
278
279 private StringProperty imageUrl = null;
280 /**
306 public String getName() {
307 return "imageUrl";
308 }
309
310 @Override
311 public CssMetaData<ImageView,String> getCssMetaData() {
312 return StyleableProperties.IMAGE;
313 }
314
315 };
316 }
317 return imageUrl;
318 }
319
320 private final AbstractNotifyListener platformImageChangeListener =
321 new AbstractNotifyListener() {
322 @Override
323 public void invalidated(Observable valueModel) {
324 invalidateWidthHeight();
325 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_CONTENTS);
326 NodeHelper.geomChanged(ImageView.this);
327 }
328 };
329 /**
330 * The current x coordinate of the {@code ImageView} origin.
331 *
332 * @defaultValue 0
333 */
334 private DoubleProperty x;
335
336
337 public final void setX(double value) {
338 xProperty().set(value);
339 }
340
341 public final double getX() {
342 return x == null ? 0.0 : x.get();
343 }
344
345 public final DoubleProperty xProperty() {
346 if (x == null) {
347 x = new DoublePropertyBase() {
348
349 @Override
350 protected void invalidated() {
351 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_GEOMETRY);
352 NodeHelper.geomChanged(ImageView.this);
353 }
354
355 @Override
356 public Object getBean() {
357 return ImageView.this;
358 }
359
360 @Override
361 public String getName() {
362 return "x";
363 }
364 };
365 }
366 return x;
367 }
368
369 /**
370 * The current y coordinate of the {@code ImageView} origin.
371 *
372 * @defaultValue 0
373 */
374 private DoubleProperty y;
375
376
377 public final void setY(double value) {
378 yProperty().set(value);
379 }
380
381 public final double getY() {
382 return y == null ? 0.0 : y.get();
383 }
384
385 public final DoubleProperty yProperty() {
386 if (y == null) {
387 y = new DoublePropertyBase() {
388
389 @Override
390 protected void invalidated() {
391 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_GEOMETRY);
392 NodeHelper.geomChanged(ImageView.this);
393 }
394
395 @Override
396 public Object getBean() {
397 return ImageView.this;
398 }
399
400 @Override
401 public String getName() {
402 return "y";
403 }
404 };
405 }
406 return y;
407 }
408
409 /**
410 * The width of the bounding box within which the source image is resized as
411 * necessary to fit. If set to a value <= 0, then the intrinsic width of the
412 * image will be used as the {@code fitWidth}.
419 */
420 private DoubleProperty fitWidth;
421
422
423 public final void setFitWidth(double value) {
424 fitWidthProperty().set(value);
425 }
426
427 public final double getFitWidth() {
428 return fitWidth == null ? 0.0 : fitWidth.get();
429 }
430
431 public final DoubleProperty fitWidthProperty() {
432 if (fitWidth == null) {
433 fitWidth = new DoublePropertyBase() {
434
435 @Override
436 protected void invalidated() {
437 invalidateWidthHeight();
438 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
439 NodeHelper.geomChanged(ImageView.this);
440 }
441
442 @Override
443 public Object getBean() {
444 return ImageView.this;
445 }
446
447 @Override
448 public String getName() {
449 return "fitWidth";
450 }
451 };
452 }
453 return fitWidth;
454 }
455
456 /**
457 * The height of the bounding box within which the source image is resized
458 * as necessary to fit. If set to a value <= 0, then the intrinsic height of
459 * the image will be used as the {@code fitHeight}.
467 */
468 private DoubleProperty fitHeight;
469
470
471 public final void setFitHeight(double value) {
472 fitHeightProperty().set(value);
473 }
474
475 public final double getFitHeight() {
476 return fitHeight == null ? 0.0 : fitHeight.get();
477 }
478
479 public final DoubleProperty fitHeightProperty() {
480 if (fitHeight == null) {
481 fitHeight = new DoublePropertyBase() {
482
483 @Override
484 protected void invalidated() {
485 invalidateWidthHeight();
486 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
487 NodeHelper.geomChanged(ImageView.this);
488 }
489
490 @Override
491 public Object getBean() {
492 return ImageView.this;
493 }
494
495 @Override
496 public String getName() {
497 return "fitHeight";
498 }
499 };
500 }
501 return fitHeight;
502 }
503
504 /**
505 * Indicates whether to preserve the aspect ratio of the source image when
506 * scaling to fit the image within the fitting bounding box.
507 * <p/>
532 */
533 private BooleanProperty preserveRatio;
534
535
536 public final void setPreserveRatio(boolean value) {
537 preserveRatioProperty().set(value);
538 }
539
540 public final boolean isPreserveRatio() {
541 return preserveRatio == null ? false : preserveRatio.get();
542 }
543
544 public final BooleanProperty preserveRatioProperty() {
545 if (preserveRatio == null) {
546 preserveRatio = new BooleanPropertyBase() {
547
548 @Override
549 protected void invalidated() {
550 invalidateWidthHeight();
551 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
552 NodeHelper.geomChanged(ImageView.this);
553 }
554
555 @Override
556 public Object getBean() {
557 return ImageView.this;
558 }
559
560 @Override
561 public String getName() {
562 return "preserveRatio";
563 }
564 };
565 }
566 return preserveRatio;
567 }
568
569 /**
570 * Indicates whether to use a better quality filtering algorithm or a faster
571 * one when transforming or scaling the source image to fit within the
572 * bounding box provided by {@code fitWidth} and {@code fitHeight}.
636 */
637 private ObjectProperty<Rectangle2D> viewport;
638
639
640 public final void setViewport(Rectangle2D value) {
641 viewportProperty().set(value);
642 }
643
644 public final Rectangle2D getViewport() {
645 return viewport == null ? null : viewport.get();
646 }
647
648 public final ObjectProperty<Rectangle2D> viewportProperty() {
649 if (viewport == null) {
650 viewport = new ObjectPropertyBase<Rectangle2D>() {
651
652 @Override
653 protected void invalidated() {
654 invalidateWidthHeight();
655 NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_VIEWPORT);
656 NodeHelper.geomChanged(ImageView.this);
657 }
658
659 @Override
660 public Object getBean() {
661 return ImageView.this;
662 }
663
664 @Override
665 public String getName() {
666 return "viewport";
667 }
668 };
669 }
670 return viewport;
671 }
672
673 // Need to track changes to image width and image height and recompute
674 // bounds when changed.
675 // imageWidth = bind image.width on replace {
676 // NodeHelper.geomChanged(ImageView.this);
677 // }
678 //
679 // imageHeight = bind image.height on replace {
680 // NodeHelper.geomChanged(ImageView.this);
681 // }
682
683 private double destWidth, destHeight;
684
685 /*
686 * Note: This method MUST only be called via its accessor method.
687 */
688 private NGNode doCreatePeer() {
689 return new NGImageView();
690 }
691
692 /*
693 * Note: This method MUST only be called via its accessor method.
694 */
695 private BaseBounds doComputeGeomBounds(BaseBounds bounds, BaseTransform tx) {
696 recomputeWidthHeight();
697
698 bounds = bounds.deriveWithNewBounds((float)getX(), (float)getY(), 0.0f,
699 (float)(getX() + destWidth), (float)(getY() + destHeight), 0.0f);
700 bounds = tx.transform(bounds, bounds);
701 return bounds;
702 }
703
704 private boolean validWH;
705
706 private void invalidateWidthHeight() {
707 validWH = false;
708 }
709
710 private void recomputeWidthHeight() {
711 if (validWH) {
712 return;
713 }
714 Image localImage = getImage();
715 Rectangle2D localViewport = getViewport();
727 double localFitWidth = getFitWidth();
728 double localFitHeight = getFitHeight();
729
730 if (isPreserveRatio() && w > 0 && h > 0 && (localFitWidth > 0 || localFitHeight > 0)) {
731 if (localFitWidth <= 0 || (localFitHeight > 0 && localFitWidth * h > localFitHeight * w)) {
732 w = w * localFitHeight / h;
733 h = localFitHeight;
734 } else {
735 h = h * localFitWidth / w;
736 w = localFitWidth;
737 }
738 } else {
739 if (localFitWidth > 0f) {
740 w = localFitWidth;
741 }
742 if (localFitHeight > 0f) {
743 h = localFitHeight;
744 }
745 }
746
747 // Store these values for use later in doComputeContains() to support
748 // Node.contains().
749 destWidth = w;
750 destHeight = h;
751
752 validWH = true;
753 }
754
755 /*
756 * Note: This method MUST only be called via its accessor method.
757 */
758 private boolean doComputeContains(double localX, double localY) {
759 if (getImage() == null) {
760 return false;
761 }
762
763 recomputeWidthHeight();
764 // Local Note bounds contain test is already done by the caller.
765 // (Node.contains()).
766
767 double dx = localX - getX();
768 double dy = localY - getY();
769
770 Image localImage = getImage();
771 double srcWidth = localImage.getWidth();
772 double srcHeight = localImage.getHeight();
773 double viewWidth = srcWidth;
774 double viewHeight = srcHeight;
775 double vw = 0;
776 double vh = 0;
777 double vminx = 0;
778 double vminy = 0;
779 Rectangle2D localViewport = getViewport();
780 if (localViewport != null) {
781 vw = localViewport.getWidth();
782 vh = localViewport.getHeight();
783 vminx = localViewport.getMinX();
784 vminy = localViewport.getMinY();
785 }
786
787 if (vw > 0 && vh > 0) {
788 viewWidth = vw;
789 viewHeight = vh;
790 }
791
792 // desWidth Note and destHeight are computed by NodeHelper.computeGeomBounds()
793 // via a call from Node.contains() before calling
794 // doComputeContains().
795 // Transform into image's coordinate system.
796 dx = vminx + dx * viewWidth / destWidth;
797 dy = vminy + dy * viewHeight / destHeight;
798 // test whether it's inside the original image AND inside of viewport
799 // (viewport may stick out from the image bounds)
800 if (dx < 0.0 || dy < 0.0 || dx >= srcWidth || dy >= srcHeight ||
801 dx < vminx || dy < vminy ||
802 dx >= vminx + viewWidth || dy >= vminy + viewHeight) {
803 return false;
804 }
805 // Do alpha test on the picked pixel.
806 return Toolkit.getToolkit().imageContains(
807 Toolkit.getImageAccessor().getPlatformImage(localImage), (float)dx, (float)dy);
808 }
809
810 /***************************************************************************
811 * * Stylesheet Handling * *
812 **************************************************************************/
813
814 private static final String DEFAULT_STYLE_CLASS = "image-view";
887 */
888 private void doUpdatePeer() {
889 final NGImageView peer = NodeHelper.getPeer(this);
890 if (NodeHelper.isDirty(this, DirtyBits.NODE_GEOMETRY)) {
891 peer.setX((float)getX());
892 peer.setY((float)getY());
893 }
894 if (NodeHelper.isDirty(this, DirtyBits.NODE_SMOOTH)) {
895 peer.setSmooth(isSmooth());
896 }
897 if (NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
898 peer.setImage(getImage() != null
899 ? Toolkit.getImageAccessor().getPlatformImage(getImage()) : null);
900 }
901 // The NG part expects this to be called when image changes
902 if (NodeHelper.isDirty(this, DirtyBits.NODE_VIEWPORT) || NodeHelper.isDirty(this, DirtyBits.NODE_CONTENTS)) {
903 updateViewport();
904 }
905 }
906
907 /*
908 * Note: This method MUST only be called via its accessor method.
909 */
910 private Object doProcessMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
911 return alg.processLeafNode(this, ctx);
912 }
913 }
|