1 /*
   2  * Copyright (c) 2010, 2016, 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 test.javafx.scene.image;
  27 
  28 import test.com.sun.javafx.pgstub.StubPlatformImageInfo;
  29 import test.com.sun.javafx.pgstub.StubPlatformImage;
  30 import test.com.sun.javafx.pgstub.StubImageLoader;
  31 import test.com.sun.javafx.pgstub.StubAsyncImageLoader;
  32 import test.com.sun.javafx.pgstub.StubImageLoaderFactory;
  33 import test.com.sun.javafx.pgstub.StubToolkit;
  34 import test.com.sun.javafx.test.PropertyInvalidationCounter;
  35 import com.sun.javafx.tk.Toolkit;
  36 import javafx.beans.InvalidationListener;
  37 import org.junit.Before;
  38 import org.junit.Test;
  39 
  40 import java.io.ByteArrayInputStream;
  41 import java.io.InputStream;
  42 import java.util.LinkedList;
  43 import java.util.Queue;
  44 import javafx.scene.image.Image;
  45 import javafx.scene.image.ImageShim;
  46 
  47 import static org.junit.Assert.*;
  48 
  49 public final class ImageTest {
  50     private final StubToolkit toolkit;
  51     private final StubImageLoaderFactory imageLoaderFactory;
  52 
  53     public ImageTest() {
  54         toolkit = (StubToolkit) Toolkit.getToolkit();
  55         imageLoaderFactory = toolkit.getImageLoaderFactory();
  56     }
  57 
  58     @Before
  59     public void setUp() {
  60         imageLoaderFactory.reset();
  61     }
  62 
  63     @Test
  64     public void loadImageFromUrlBasicTest() {
  65         final String url = "file:test.png";
  66         registerImage(url, 100, 200);
  67 
  68         final Image image = new Image(url);
  69 
  70         assertEquals(url, image.getUrl());
  71         verifyLoadedImage(image, 0, 0, false, false, 100, 200);
  72     }
  73 
  74     @Test
  75     public void loadImageFromUrlScaledTest() {
  76         final String url = "file:test.png";
  77         registerImage(url, 300, 100);
  78 
  79         final Image image = new Image(url, 200, 400, true, true);
  80 
  81         assertEquals(url, image.getUrl());
  82         verifyLoadedImage(image, 200, 400, true, true, 300, 100);
  83     }
  84 
  85     @Test
  86     public void loadImageFromMissingUrlTest() {
  87         final Image image = new Image("file:missing.png");
  88 
  89         assertTrue(image.isError());
  90     }
  91 
  92     @Test
  93     public void loadImageFromStreamBasicTest() {
  94         final InputStream is = new ByteArrayInputStream(new byte[0]);
  95         registerImage(is, 100, 200);
  96 
  97         final Image image = new Image(is);
  98 
  99         assertEquals(is, ImageShim.getInputSource(image));
 100         verifyLoadedImage(image, 0, 0, false, false, 100, 200);
 101     }
 102 
 103     @Test
 104     public void loadImageFromStreamScaledTest() {
 105         final InputStream is = new ByteArrayInputStream(new byte[0]);
 106         registerImage(is, 300, 100);
 107 
 108         final Image image = new Image(is, 200, 400, true, true);
 109 
 110         assertEquals(is, ImageShim.getInputSource(image));
 111         verifyLoadedImage(image, 200, 400, true, true, 300, 100);
 112     }
 113 
 114     @Test
 115     public void fromPlatformImageTest() {
 116         final Object fakePlatformImage = new Object();
 117         registerImage(fakePlatformImage, 200, 500);
 118 
 119         final Image image = Image.impl_fromPlatformImage(fakePlatformImage);
 120         verifyLoadedImage(image, 0, 0, false, false, 200, 500);
 121     }
 122 
 123     @Test
 124     public void loadImageAsyncProgressTest() {
 125         final String url = "file:test.png";
 126         registerImage(url, 200, 100);
 127 
 128         final Image image = new Image(url, true);
 129 
 130         final StubAsyncImageLoader lastAsyncImageLoader =
 131                 getLastAsyncImageLoader();
 132 
 133         lastAsyncImageLoader.setProgress(0, 100);
 134         final float p1 = (float) image.getProgress();
 135 
 136         lastAsyncImageLoader.setProgress(33, 100);
 137         final float p2 = (float) image.getProgress();
 138 
 139         lastAsyncImageLoader.setProgress(66, 100);
 140         final float p3 = (float) image.getProgress();
 141 
 142         lastAsyncImageLoader.setProgress(200, 100);
 143         final float p4 = (float) image.getProgress();
 144 
 145         lastAsyncImageLoader.finish();
 146 
 147         assertTrue(p1 < p2);
 148         assertTrue(p2 < p3);
 149         assertTrue(p3 == p4);
 150     }
 151 
 152     /*
 153     @Test
 154     public void loadImageAsyncPlaceholderTest() {
 155         final Object fakePlatformImage1 = new Object();
 156         registerImage(fakePlatformImage1, 200, 500);
 157         final Object fakePlatformImage2 = new Object();
 158         registerImage(fakePlatformImage2, 200, 500);
 159 
 160         final String url = "file:test.png";
 161         registerImage(url, 200, 100);
 162 
 163         final Image placeholderImage1 =
 164                 Image.impl_fromPlatformImage(fakePlatformImage1);
 165         final Image placeholderImage2 =
 166                 Image.impl_fromPlatformImage(fakePlatformImage2);
 167         final Image image = new Image(url, 500, 200, true, false,
 168                                       true);
 169 
 170         final StubAsyncImageLoader lastAsyncImageLoader =
 171                 getLastAsyncImageLoader();
 172 
 173         final Object platformImage1 = getPlatformImage(image);
 174         final Object platformImage2 = getPlatformImage(image);
 175         lastAsyncImageLoader.finish();
 176         final Object platformImage3 = getPlatformImage(image);
 177         final Object platformImage4 = getPlatformImage(image);
 178 
 179         lastAsyncImageLoader.finish();
 180 
 181         assertNotSame(platformImage1, platformImage2);
 182         assertNotSame(platformImage2, platformImage3);
 183         assertNotSame(platformImage1, platformImage3);
 184         assertSame(platformImage3, platformImage4);
 185 
 186         verifyLoadedImage(image, 500, 200, true, false, 200, 100);
 187     }
 188 
 189     @Test
 190     public void loadImageAsyncWithAsyncPlaceholderTest() {
 191         final String placeholderUrl = "file:placeholder.png";
 192         registerImage(placeholderUrl, 200, 100);
 193         final String finalUrl = "file:final.png";
 194         registerImage(finalUrl, 100, 200);
 195 
 196         final Image placeholderImage =
 197                 new Image(placeholderUrl, true);
 198         final StubAsyncImageLoader placeholderLoader =
 199                 getLastAsyncImageLoader();
 200 
 201         final Image finalImage =
 202                 new Image(finalUrl, true);
 203         final StubAsyncImageLoader finalLoader =
 204                 getLastAsyncImageLoader();
 205 
 206         assertNull(finalImage.impl_getPlatformImage());
 207         assertEquals(0, finalImage.getWidth(), 0);
 208         assertEquals(0, finalImage.getHeight(), 0);
 209 
 210         placeholderLoader.finish();
 211         assertEquals(placeholderUrl, getPlatformImage(finalImage).getSource());
 212         assertEquals(200, finalImage.getWidth(), 0);
 213         assertEquals(100, finalImage.getHeight(), 0);
 214 
 215         finalLoader.finish();
 216         assertEquals(finalUrl, getPlatformImage(finalImage).getSource());
 217         assertEquals(100, finalImage.getWidth(), 0);
 218         assertEquals(200, finalImage.getHeight(), 0);
 219     }
 220     */
 221 
 222     @Test
 223     public void loadImageAsyncCancelTest() {
 224         final String url = "file:test.png";
 225         registerImage(url, 200, 100);
 226 
 227         final Image image1 = new Image(url, true);
 228         final StubAsyncImageLoader lastAsyncImageLoader =
 229                 getLastAsyncImageLoader();
 230 
 231         image1.cancel();
 232         assertTrue(lastAsyncImageLoader.isCancelled());
 233         assertTrue(image1.isError());
 234 
 235         final Image image2 = new Image(url);
 236         image2.cancel();
 237         verifyLoadedImage(image2, 0, 0, false, false, 200, 100);
 238     }
 239 
 240     @Test
 241     public void loadImageAsyncErrorTest() {
 242         final String url = "file:test.png";
 243         registerImage(url, 200, 100);
 244 
 245         final Image image = new Image(url, true);
 246 
 247         final StubAsyncImageLoader lastAsyncImageLoader =
 248                 getLastAsyncImageLoader();
 249 
 250         final Exception testException = new Exception("Test exception");
 251 
 252         lastAsyncImageLoader.finish(testException);
 253         assertTrue(image.isError());
 254         assertEquals(testException, image.getException());
 255     }
 256 
 257     @Test
 258     public void loadMultipleImagesAsyncTest() {
 259         final int multiImageCount = 100;
 260         final Queue<StubAsyncImageLoader> asyncLoaders =
 261                 new LinkedList<StubAsyncImageLoader>();
 262         final Image[] images = new Image[multiImageCount];
 263 
 264         StubAsyncImageLoader lastAsyncLoader = null;
 265         for (int i = 0; i < multiImageCount; ++i) {
 266             final String url = "file:multi" + i + ".png";
 267             registerImage(url, 100, 100);
 268 
 269             images[i] = new Image(url, true);
 270 
 271             StubAsyncImageLoader asyncLoader =
 272                     imageLoaderFactory.getLastAsyncImageLoader();
 273             assertNotNull(asyncLoader);
 274             if (lastAsyncLoader == asyncLoader) {
 275                 asyncLoaders.poll().finish();
 276                 asyncLoader = imageLoaderFactory.getLastAsyncImageLoader();
 277                 assertNotSame(lastAsyncLoader, asyncLoader);
 278             }
 279 
 280             asyncLoaders.add(asyncLoader);
 281             lastAsyncLoader = asyncLoader;
 282         }
 283 
 284         for (final StubAsyncImageLoader asyncLoader: asyncLoaders) {
 285             asyncLoader.finish();
 286         }
 287 
 288         for (final Image image: images) {
 289             verifyLoadedImage(image, 0, 0, false, false, 100, 100);
 290         }
 291     }
 292 
 293     @Test
 294     public void loadMultipleImagesAsyncCancelTest() {
 295         final int multiImageCount = 100;
 296         final Image[] images = new Image[multiImageCount];
 297 
 298         for (int i = 0; i < multiImageCount; ++i) {
 299             final String url = "file:multi_cancel_" + i + ".png";
 300             registerImage(url, 100, 100);
 301 
 302             images[i] = new Image(url, true);
 303         }
 304 
 305         // traverse backwards to first cancel images which were queued because
 306         // of the thread limit
 307         for (int i = images.length - 1; i >= 0; --i) {
 308             final Image image = images[i];
 309             image.cancel();
 310             assertTrue(image.isError());
 311         }
 312     }
 313 
 314     @Test
 315     public void animatedImageTest() {
 316         // reset time
 317         toolkit.setAnimationTime(0);
 318         final Image animatedImage =
 319                 TestImages.createAnimatedTestImage(
 320                         300, 400,        // width, height
 321                         0,               // loop count
 322                         2000, 1000, 3000 // frame delays
 323                 );
 324 
 325         verifyLoadedImage(animatedImage, 0, 0, false, false, 300, 400);
 326 
 327         toolkit.setAnimationTime(1000);
 328         assertEquals(0, getPlatformImage(animatedImage).getFrame());
 329 
 330         toolkit.setAnimationTime(1999);
 331         assertEquals(0, getPlatformImage(animatedImage).getFrame());
 332         toolkit.setAnimationTime(2000);
 333         assertEquals(1, getPlatformImage(animatedImage).getFrame());
 334 
 335         toolkit.setAnimationTime(2500);
 336         assertEquals(1, getPlatformImage(animatedImage).getFrame());
 337 
 338         toolkit.setAnimationTime(2999);
 339         assertEquals(1, getPlatformImage(animatedImage).getFrame());
 340         toolkit.setAnimationTime(3000);
 341         assertEquals(2, getPlatformImage(animatedImage).getFrame());
 342 
 343         toolkit.setAnimationTime(4500);
 344         assertEquals(2, getPlatformImage(animatedImage).getFrame());
 345 
 346         toolkit.setAnimationTime(5999);
 347         assertEquals(2, getPlatformImage(animatedImage).getFrame());
 348         toolkit.setAnimationTime(6000);
 349         // end of the cycle does flip frames!
 350         assertEquals(0, getPlatformImage(animatedImage).getFrame());
 351 
 352         toolkit.setAnimationTime(7000);
 353         assertEquals(0, getPlatformImage(animatedImage).getFrame());
 354 
 355         TestImages.disposeAnimatedImage(animatedImage);
 356     }
 357 
 358     @Test
 359     public void animatedImageTestLoopOnce() {
 360         // reset time
 361         toolkit.setAnimationTime(0);
 362         final Image animatedImage =
 363                 TestImages.createAnimatedTestImage(
 364                         300, 400,        // width, height
 365                         1,               // loop count
 366                         2000, 1000, 3000 // frame delays
 367                 );
 368 
 369         verifyLoadedImage(animatedImage, 0, 0, false, false, 300, 400);
 370 
 371         toolkit.setAnimationTime(1000);
 372         assertEquals(0, getPlatformImage(animatedImage).getFrame());
 373 
 374         toolkit.setAnimationTime(2500);
 375         assertEquals(1, getPlatformImage(animatedImage).getFrame());
 376 
 377         toolkit.setAnimationTime(4500);
 378         assertEquals(2, getPlatformImage(animatedImage).getFrame());
 379 
 380         toolkit.setAnimationTime(7000);
 381         assertEquals(2, getPlatformImage(animatedImage).getFrame());
 382 
 383         TestImages.disposeAnimatedImage(animatedImage);
 384     }
 385 
 386     @Test
 387     public void imagePropertyListenersCalledOnceTest() {
 388         final String url = "file:test.png";
 389         registerImage(url, 200, 100);
 390 
 391         final Image image = new Image(url, true);
 392 
 393         final StubAsyncImageLoader lastAsyncImageLoader =
 394                 getLastAsyncImageLoader();
 395 
 396         final PropertyInvalidationCounter<Number> widthInvalidationCounter =
 397                 new PropertyInvalidationCounter<Number>();
 398         final PropertyInvalidationCounter<Number> heightInvalidationCounter =
 399                 new PropertyInvalidationCounter<Number>();
 400         final PropertyInvalidationCounter<Object> plImageInvalidationCounter =
 401                 new PropertyInvalidationCounter<Object>();
 402 
 403         image.widthProperty().addListener(widthInvalidationCounter);
 404         image.heightProperty().addListener(heightInvalidationCounter);
 405         Toolkit.getImageAccessor().getImageProperty(image).addListener(plImageInvalidationCounter);
 406 
 407         assertEquals(0, image.getWidth(), 0);
 408         assertEquals(0, image.getHeight(), 0);
 409         assertEquals(null, image.impl_getPlatformImage());
 410 
 411         lastAsyncImageLoader.finish();
 412 
 413         assertEquals(1, widthInvalidationCounter.getCounter());
 414         assertEquals(1, heightInvalidationCounter.getCounter());
 415         assertEquals(1, plImageInvalidationCounter.getCounter());
 416     }
 417 
 418     @Test
 419     public void imagePropertiesChangedAtomicallyTest() {
 420         final String url = "file:test.png";
 421         registerImage(url, 200, 100);
 422 
 423         final Image image = new Image(url, true);
 424         final StubAsyncImageLoader lastAsyncImageLoader =
 425                 getLastAsyncImageLoader();
 426 
 427         final InvalidationListener imageChecker =
 428                 observable -> {
 429                     assertEquals(200, image.getWidth(), 0);
 430                     assertEquals(100, image.getHeight(), 0);
 431                     assertNotNull(image.impl_getPlatformImage());
 432                 };
 433 
 434         image.widthProperty().addListener(imageChecker);
 435         image.heightProperty().addListener(imageChecker);
 436         Toolkit.getImageAccessor().getImageProperty(image).addListener(imageChecker);
 437 
 438         assertEquals(0, image.getWidth(), 0);
 439         assertEquals(0, image.getHeight(), 0);
 440         assertEquals(null, image.impl_getPlatformImage());
 441 
 442         lastAsyncImageLoader.finish();
 443     }
 444 
 445     private static void verifyLoadedImage(final Image image,
 446                                           final int loadWidth,
 447                                           final int loadHeight,
 448                                           final boolean preserveRatio,
 449                                           final boolean smooth,
 450                                           final int imageWidth,
 451                                           final int imageHeight) {
 452         assertFalse(image.isError());
 453         assertEquals(1, (int) image.getProgress());
 454 
 455         final StubPlatformImage platformImage =
 456                 getPlatformImage(image);
 457         assertEquals(0, platformImage.getFrame());
 458 
 459         final StubImageLoader imageLoader =
 460                 platformImage.getImageLoader();
 461         assertEquals(loadWidth, imageLoader.getLoadWidth());
 462         assertEquals(loadHeight, imageLoader.getLoadHeight());
 463         assertEquals(preserveRatio, imageLoader.getPreserveRatio());
 464         assertEquals(smooth, imageLoader.getSmooth());
 465 
 466         assertEquals(imageWidth, (int) image.getWidth());
 467         assertEquals(imageHeight, (int) image.getHeight());
 468     }
 469 
 470     private static StubPlatformImage getPlatformImage(final Image srcImage) {
 471         final Object unknownPlatformImage = srcImage.impl_getPlatformImage();
 472         assertTrue(unknownPlatformImage instanceof StubPlatformImage);
 473         return (StubPlatformImage) unknownPlatformImage;
 474     }
 475 
 476     private void registerImage(final Object source,
 477                                final int width,
 478                                final int height) {
 479         imageLoaderFactory.registerImage(
 480                 source, new StubPlatformImageInfo(width, height));
 481     }
 482 
 483     private StubAsyncImageLoader getLastAsyncImageLoader() {
 484         final StubAsyncImageLoader lastAsyncImageLoader =
 485                 imageLoaderFactory.getLastAsyncImageLoader();
 486         assertNotNull(lastAsyncImageLoader);
 487         assertTrue(lastAsyncImageLoader.isStarted());
 488 
 489         return lastAsyncImageLoader;
 490     }
 491 
 492     @Test
 493     public void createImageFromClasspathTest() {
 494         final String url = "test/javafx/scene/image/test.png";
 495         final String resolvedUrl = Thread.currentThread().getContextClassLoader().getResource(url).toString();
 496         registerImage(resolvedUrl, 100, 200);
 497 
 498         final Image image = new Image(url);
 499 
 500         assertEquals(resolvedUrl, image.getUrl());
 501         verifyLoadedImage(image, 0, 0, false, false, 100, 200);
 502     }
 503 
 504     @Test
 505     public void createImageFromClasspathTest_withLeadingSlash() {
 506         final String url = "/test/javafx/scene/image/test.png";
 507         final String resolvedUrl = Thread.currentThread().getContextClassLoader().getResource(url.substring(1)).toString();
 508         registerImage(resolvedUrl, 100, 200);
 509 
 510         final Image image = new Image(url);
 511 
 512         assertEquals(resolvedUrl, image.getUrl());
 513         verifyLoadedImage(image, 0, 0, false, false, 100, 200);
 514     }
 515 
 516     @Test(expected=NullPointerException.class)
 517     public void createImageFromNullUrlTest() {
 518         new Image((String) null);
 519     }
 520 
 521     @Test(expected=NullPointerException.class)
 522     public void createImageAsyncFromNullUrlTest() {
 523         new Image(null, true);
 524     }
 525 
 526     @Test(expected=NullPointerException.class)
 527     public void createImageFromNullInputStreamTest() {
 528         new Image((InputStream) null);
 529     }
 530 
 531     @Test(expected=IllegalArgumentException.class)
 532     public void createImageFromEmptyUrlTest() {
 533         new Image("");
 534     }
 535 
 536     @Test(expected=IllegalArgumentException.class)
 537     public void createImageAsyncFromEmptyUrlTest() {
 538         new Image("", true);
 539     }
 540 
 541     @Test(expected=IllegalArgumentException.class)
 542     public void createImageFromInvalidUrlTest() {
 543         new Image(":");
 544     }
 545 
 546     @Test(expected=IllegalArgumentException.class)
 547     public void createImageAsyncFromInvalidUrlTest() {
 548         new Image(":", true);
 549     }
 550 
 551     @Test(expected=IllegalArgumentException.class)
 552     public void createImageFromUnsupportedUrlTest() {
 553         new Image("unsupported:image.png");
 554     }
 555 
 556     @Test(expected=IllegalArgumentException.class)
 557     public void createImageAsyncFromUnsupportedUrlTest() {
 558         new Image("unsupported:image.png", true);
 559     }
 560 }