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