1 /* 2 * Copyright (c) 2018, 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 package test.robot.javafx.scene.canvas; 26 27 import java.io.FileInputStream; 28 import java.net.URL; 29 30 import javafx.application.Application; 31 import javafx.application.Platform; 32 import javafx.scene.Scene; 33 import javafx.scene.canvas.Canvas; 34 import javafx.scene.canvas.GraphicsContext; 35 import javafx.scene.image.Image; 36 import javafx.scene.layout.VBox; 37 import javafx.scene.paint.Color; 38 import javafx.scene.robot.Robot; 39 import javafx.stage.Stage; 40 import javafx.stage.StageStyle; 41 import javafx.stage.WindowEvent; 42 43 import java.util.concurrent.CountDownLatch; 44 import java.util.concurrent.TimeUnit; 45 46 import org.junit.AfterClass; 47 import org.junit.Assert; 48 import org.junit.BeforeClass; 49 import org.junit.Test; 50 import static org.junit.Assert.fail; 51 52 import test.util.Util; 53 54 // 1. An image, half white half black sized 10 X 10 is used in test. 55 // 2. The image is drawn on a 20 times scaled up canvas(200 X 200). 56 57 public class ImageSmoothingDrawTest { 58 59 static CountDownLatch startupLatch = new CountDownLatch(1); 60 static Robot robot; 61 static ImageCanvas imageCanvas; 62 static volatile Stage stage; 63 static volatile Scene scene; 64 Color lastWhitePixelColor; 65 Color whitePixelColor; 66 static final int scaleFactor = 20; 67 68 public void getPixelColors() { 69 int lastWhiteX = (int) (stage.getX() + scene.getX() + 70 imageCanvas.getLayoutX() + (imageCanvas.getWidth() / 2) - 2); 71 int heightCenter = (int) (stage.getY() + scene.getY() + 72 imageCanvas.getLayoutY() + (imageCanvas.getHeight() / 2)); 73 74 Util.runAndWait(() -> { 75 lastWhitePixelColor = robot.getPixelColor(lastWhiteX, heightCenter); 76 whitePixelColor = robot.getPixelColor( 77 lastWhiteX - (imageCanvas.getWidth() / 4), heightCenter); 78 }); 79 } 80 81 // 3. When image smoothing is enabled, linear filtering is applied 82 // at the center where white and black colors meet. 83 // 4. A width of few pixels at the center would not be white or black. 84 // 5. Color of second last white side pixel, should NOT be white. 85 @Test 86 public void testImageSmoothingEnabled() { 87 imageCanvas.setImageSmoothing(true); 88 Util.sleep(1000); // Wait for image to be drawn 89 getPixelColors(); 90 Assert.assertEquals(Color.WHITE, whitePixelColor); 91 Assert.assertFalse(whitePixelColor.equals(lastWhitePixelColor)); 92 } 93 94 // 6. When image smoothing is disabled, filtering is NOT applied 95 // where at the center where white and black colors meet. 96 // 7. As filtering is NOT applied, all white pixels remain white 97 // and black remain black. 98 // 8. Color of second last white side pixel, should remain white. 99 @Test 100 public void testImageSmoothingDisabled() { 101 imageCanvas.setImageSmoothing(false); 102 Util.sleep(1000); // Wait for image to be drawn 103 getPixelColors(); 104 Assert.assertEquals(Color.WHITE, whitePixelColor); 105 Assert.assertEquals(whitePixelColor, lastWhitePixelColor); 106 } 107 108 @BeforeClass 109 public static void initFX() { 110 new Thread(() -> Application.launch(TestApp.class, (String[])null)).start(); 111 waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start"); 112 } 113 114 @AfterClass 115 public static void exit() { 116 Platform.runLater(() -> { 117 stage.hide(); 118 }); 119 Platform.exit(); 120 } 121 122 public static class TestApp extends Application { 123 @Override 124 public void start(Stage primaryStage) throws Exception { 125 robot = new Robot(); 126 stage = primaryStage; 127 // Create 20 times scaled canvas. 128 URL resource = this.getClass().getResource("image_smoothing_draw_test.png"); 129 FileInputStream inFile = new FileInputStream(resource.getFile()); 130 Image whiteBlack = new Image(inFile); 131 inFile.close(); 132 imageCanvas = new ImageCanvas(whiteBlack, 133 whiteBlack.getWidth() * scaleFactor, 134 whiteBlack.getHeight() * scaleFactor); 135 136 VBox root = new VBox(); 137 root.getChildren().add(imageCanvas); 138 139 scene = new Scene(root); 140 stage.setScene(scene); 141 stage.initStyle(StageStyle.UNDECORATED); 142 stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> 143 Platform.runLater(startupLatch::countDown)); 144 stage.setAlwaysOnTop(true); 145 stage.show(); 146 } 147 } 148 149 public static void waitForLatch(CountDownLatch latch, int seconds, String msg) { 150 try { 151 if (!latch.await(seconds, TimeUnit.SECONDS)) { 152 fail(msg); 153 } 154 } catch (Exception ex) { 155 fail("Unexpected exception: " + ex); 156 } 157 } 158 159 static class ImageCanvas extends Canvas { 160 private Image image = null; 161 162 public ImageCanvas(Image img, double width, double height) { 163 super(width, height); 164 image = img; 165 } 166 167 public void render() { 168 GraphicsContext gc = getGraphicsContext2D(); 169 if (image != null) { 170 gc.drawImage(image, 171 0, 0, image.getWidth(), image.getHeight(), 172 0, 0, getWidth(), getHeight()); 173 } 174 } 175 176 public void setImageSmoothing(boolean smooth) { 177 GraphicsContext gc = getGraphicsContext2D(); 178 gc.setImageSmoothing(smooth); 179 render(); 180 } 181 } 182 }