8027708: NASHORN TEST: Create Nashorn test that draws image step-by-step using JavaFX canvas.
authorkshefov
Tue, 05 Nov 2013 13:09:40 +0400
changeset 21690 ffbb4611d1f4
parent 21689 420b41bb7ab7
child 21692 e1c40a93dd66
8027708: NASHORN TEST: Create Nashorn test that draws image step-by-step using JavaFX canvas. Reviewed-by: jlaskey, lagergren
nashorn/make/build.xml
nashorn/make/project.properties
nashorn/test/script/jfx.js
nashorn/test/script/jfx/flyingimage.js
nashorn/test/script/jfx/flyingimage/flyingimage.png
nashorn/test/script/jfx/flyingimage/golden/linux.png
nashorn/test/script/jfx/flyingimage/golden/macosx.png
nashorn/test/script/jfx/flyingimage/golden/windows.png
nashorn/test/script/jfx/kaleidoscope.js
nashorn/test/script/jfx/kaleidoscope/golden/linux.png
nashorn/test/script/jfx/kaleidoscope/golden/macosx.png
nashorn/test/script/jfx/kaleidoscope/golden/windows.png
nashorn/test/script/jfx/spread.js
nashorn/test/script/jfx/spread/golden/linux.png
nashorn/test/script/jfx/spread/golden/macosx.png
nashorn/test/script/jfx/spread/golden/windows.png
--- a/nashorn/make/build.xml	Tue Nov 05 09:13:41 2013 +0530
+++ b/nashorn/make/build.xml	Tue Nov 05 13:09:40 2013 +0400
@@ -372,6 +372,12 @@
     
     <copy file="${file.reference.jfxrt.jar}" todir="dist"/>
     
+    <condition property="jfx.prism.order" value="-Dprism.order=j2d" else=" ">
+		<not>
+            <os family="mac"/>
+        </not>
+	</condition>
+    
     <testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
        verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
       <jvmarg line="${ext.class.path}"/>
@@ -380,6 +386,7 @@
         <propertyref prefix="testjfx-test-sys-prop."/>
         <mapper from="testjfx-test-sys-prop.*" to="*" type="glob"/>
       </propertyset>
+      <sysproperty key="test.fork.jvm.options" value="${testjfx-test-sys-prop.test.fork.jvm.options} ${jfx.prism.order}"/>
       <classpath>
           <pathelement path="${testjfx.run.test.classpath}"/>
       </classpath>
--- a/nashorn/make/project.properties	Tue Nov 05 09:13:41 2013 +0530
+++ b/nashorn/make/project.properties	Tue Nov 05 13:09:40 2013 +0400
@@ -230,7 +230,7 @@
     ${file.reference.jemmyawtinput.jar}${path.separator}\
     ${file.reference.testng.jar}${path.separator}\
     ${nashorn.internal.tests.jar}${path.separator}\
-    ${nashorn.api.tests.jar}    
+    ${nashorn.api.tests.jar}
 
 # testjfx VM options for script tests with @fork option
 testjfx-test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} -cp ${testjfx.run.test.classpath}
--- a/nashorn/test/script/jfx.js	Tue Nov 05 09:13:41 2013 +0530
+++ b/nashorn/test/script/jfx.js	Tue Nov 05 13:09:40 2013 +0400
@@ -37,13 +37,24 @@
 var Scene                = Java.type("javafx.scene.Scene");
 var Stage                = Java.type("javafx.stage.Stage");
 var File                 = Java.type("java.io.File");
-var Timer                = Java.type("java.util.Timer");
-var TimerTask            = Java.type("java.util.TimerTask");
 var OSInfo               = Java.type("sun.awt.OSInfo");
 var OSType               = Java.type("sun.awt.OSInfo.OSType");
 var StringBuffer         = Java.type("java.lang.StringBuffer");
+var Paint                = Java.type("javafx.scene.paint.Paint");
+var Color                = Java.type("javafx.scene.paint.Color");
+var Image                = Java.type("javafx.scene.image.Image");
+var Canvas               = Java.type("javafx.scene.canvas.Canvas");
+var BorderPane           = Java.type("javafx.scene.layout.BorderPane");
+var StackPane            = Java.type("javafx.scene.layout.StackPane");
+var StrokeLineCap        = Java.type("javafx.scene.shape.StrokeLineCap");
+var Platform             = Java.type("javafx.application.Platform");
+var Runnable             = Java.type("java.lang.Runnable");
+var RunnableExtend       = Java.extend(Runnable);
+var AnimationTimer       = Java.type("javafx.animation.AnimationTimer");
+var AnimationTimerExtend = Java.extend(AnimationTimer);
+var Timer                = Java.type("java.util.Timer");
+var TimerTask            = Java.type("java.util.TimerTask");
 
-var WAIT = 2000;
 var TESTNAME = "test";
 var fsep = System.getProperty("file.separator");
 
@@ -53,14 +64,16 @@
         run: function run() {
             var tmpdir = System.getProperty("java.io.tmpdir");
             var timenow = (new Date()).getTime();
-            makeScreenShot(tmpdir + fsep + "screenshot" + timenow +".png");
-            var dupImg = isDuplicateImages(tmpdir + fsep + "screenshot" + timenow +".png", __DIR__ + "jfx" + fsep + TESTNAME + fsep + "golden");
-            (new File(mpdir + fsep + "screenshot" + timenow +".png")).delete();
-            if (!dupImg) System.err.println("ERROR: screenshot does not match golden image");
+            var scrShotTmp = tmpdir + fsep + "screenshot" + timenow +".png";
+            var goldenImageDir = __DIR__ + "jfx" + fsep + TESTNAME + fsep + "golden";
+            makeScreenShot(scrShotTmp);
+            var dupImg = isDuplicateImages(scrShotTmp, goldenImageDir);
+            (new File(scrShotTmp)).delete();
+            if (!dupImg) System.err.println("ERROR: screenshot does not match the golden image");
             exit(0);
         }
     };
-    raceTimer.schedule(timerTask, WAIT);
+    raceTimer.schedule(timerTask, 100);
 }
 
 function makeScreenShot(shootToImg) {
@@ -70,10 +83,10 @@
    imageJemmy.save(shootToImg);
 }
 
-function isDuplicateImages(file1, file2) {
-    var f1 = new File(file1);
+function isDuplicateImages(screenShot, goldenDir) {
+    var f1 = new File(screenShot);
     var f2;
-    var sb = new StringBuffer(file2);
+    var sb = new StringBuffer(goldenDir);
     if (OSInfo.getOSType() == OSType.WINDOWS) {
         f2 = new File(sb.append(fsep + "windows.png").toString());
     } else if (OSInfo.getOSType() == OSType.LINUX) {
@@ -81,8 +94,6 @@
     } else if (OSInfo.getOSType() == OSType.MACOSX) {
         f2 = new File(sb.append(fsep + "macosx.png").toString());
     }
-    print(f1.getAbsolutePath());
-    print(f2.getAbsolutePath());
     if (f1.exists() && f2.exists()) {
         var image1 = new AWTImage(PNGDecoder.decode(f1.getAbsolutePath()));
         var image2 = new AWTImage(PNGDecoder.decode(f2.getAbsolutePath()));
--- a/nashorn/test/script/jfx/flyingimage.js	Tue Nov 05 09:13:41 2013 +0530
+++ b/nashorn/test/script/jfx/flyingimage.js	Tue Nov 05 13:09:40 2013 +0400
@@ -31,15 +31,6 @@
 
 TESTNAME = "flyingimage";
 
-var Image                = Java.type("javafx.scene.image.Image");
-var Color                = Java.type("javafx.scene.paint.Color");
-var Canvas               = Java.type("javafx.scene.canvas.Canvas");
-var BorderPane           = Java.type("javafx.scene.layout.BorderPane");
-var StackPane            = Java.type("javafx.scene.layout.StackPane");
-var Font                 = Java.type("javafx.scene.text.Font");
-var FontSmoothingType    = Java.type("javafx.scene.text.FontSmoothingType");
-var Text                 = Java.type("javafx.scene.text.Text");
-
 var WIDTH = 800;
 var HEIGHT = 600;
 var canvas = new Canvas(WIDTH, HEIGHT);
@@ -48,10 +39,9 @@
 }
 var imageUrl = fileToURL(__DIR__ + "flyingimage/flyingimage.png");
 var img = new Image(imageUrl);
-var font = new Font("Arial", 16);
-var t = 0;
 var isFrameRendered = false;
 function renderFrame() {
+    var t = frame;
     var gc = canvas.graphicsContext2D;
     gc.setFill(Color.web("#cccccc"));
     gc.fillRect(0, 0, WIDTH, HEIGHT);
@@ -61,7 +51,7 @@
     var c = 200;
     var msc= 0.5 * HEIGHT / img.height;
     var sp0 = 0.003;
-    for (var h = 0; h < c; h++, t++) {
+    for (var h = 0; h < c; h++) {
         gc.setTransform(1, 0, 0, 1, 0, 0);
         var yh = h / (c - 1);
         gc.translate((0.5 + Math.sin(t * sp0 + h * 0.1) / 3) * WIDTH, 25 + (HEIGHT * 3 / 4 - 40) * (yh * yh));
@@ -69,15 +59,26 @@
         gc.rotate(90 * Math.sin(t * sp0 + h * 0.1 + Math.PI));
         gc.scale(sc, sc);
         gc.drawImage(img, -img.width / 2, -img.height / 2);
-     }
+    }
     gc.setTransform(1, 0, 0, 1, 0, 0);
     isFrameRendered = true;
 }
 var stack = new StackPane();
 var pane = new BorderPane();
-
 pane.setCenter(canvas);
 stack.getChildren().add(pane);
 $STAGE.scene = new Scene(stack);
-renderFrame();
-checkImageAndExit();
+var frame = 0;
+var timer = new AnimationTimerExtend() {
+    handle: function handle(now) {
+        if (frame < 200) {
+            renderFrame();
+            frame++;
+        } else {
+            checkImageAndExit();        
+            timer.stop();
+        }
+    }
+};
+timer.start();
+ 
Binary file nashorn/test/script/jfx/flyingimage/flyingimage.png has changed
Binary file nashorn/test/script/jfx/flyingimage/golden/linux.png has changed
Binary file nashorn/test/script/jfx/flyingimage/golden/macosx.png has changed
Binary file nashorn/test/script/jfx/flyingimage/golden/windows.png has changed
--- a/nashorn/test/script/jfx/kaleidoscope.js	Tue Nov 05 09:13:41 2013 +0530
+++ b/nashorn/test/script/jfx/kaleidoscope.js	Tue Nov 05 13:09:40 2013 +0400
@@ -30,13 +30,6 @@
  */
 
 TESTNAME = "kaleidoscope";
-WAIT = 4000;
-
-var Paint                = Java.type("javafx.scene.paint.Paint");
-var Canvas               = Java.type("javafx.scene.canvas.Canvas");
-var BorderPane           = Java.type("javafx.scene.layout.BorderPane");
-var StackPane            = Java.type("javafx.scene.layout.StackPane");
-var StrokeLineCap        = Java.type("javafx.scene.shape.StrokeLineCap");
         
 var WIDTH = 800;
 var HEIGHT = 600;
@@ -56,26 +49,28 @@
 var r,e;
 var fade;
 var prv_x,prv_y,prv_x2,prv_y2;
+var isFrameRendered = false;
 
 function renderFrame() {
-	a=0.2*angle;
-	b=0.7*angle;
-	r=0;
-	fade=32;
-	for(var i=0;i<6;i++)
-		{
-		c[i]=1.0/(i+1)/2;
-		d[i]=1.0/(i+1)/2;
-		}
-	radius=Math.round((WIDTH+HEIGHT)/8);
-	e=radius*0.2;
-	p_x=Math.round(WIDTH/2);
-	p_y=Math.round(HEIGHT/2);
-	x=(radius*c[0])*Math.cos(a*d[1])+(radius*c[2])*Math.sin(a*d[3])+(radius*c[4])*Math.sin(a*d[5]);
-	y=(radius*c[5])*Math.sin(a*d[4])+(radius*c[3])*Math.cos(a*d[2])+(radius*c[1])*Math.cos(a*d[0]);
-    for (i = 0; i < 800; i++) {
-        anim();
+	if (!isFrameRendered) {
+        a=0.2*angle;
+		b=0.7*angle;
+		r=0;
+		fade=32;
+		for(var i=0;i<6;i++)
+			{
+			c[i]=1.0/(i+1)/2;
+			d[i]=1.0/(i+1)/2;
+			}
+		radius=Math.round((WIDTH+HEIGHT)/8);
+		e=radius*0.2;
+		p_x=Math.round(WIDTH/2);
+		p_y=Math.round(HEIGHT/2);
+		x=(radius*c[0])*Math.cos(a*d[1])+(radius*c[2])*Math.sin(a*d[3])+(radius*c[4])*Math.sin(a*d[5]);
+		y=(radius*c[5])*Math.sin(a*d[4])+(radius*c[3])*Math.cos(a*d[2])+(radius*c[1])*Math.cos(a*d[0]);
+        isFrameRendered = true;
     }
+    anim();
 }
 
 function anim() {
@@ -154,9 +149,19 @@
 
 var stack = new StackPane();
 var pane = new BorderPane();
-
 pane.setCenter(canvas);
 stack.getChildren().add(pane);
 $STAGE.scene = new Scene(stack);
-renderFrame();
-checkImageAndExit();
\ No newline at end of file
+var frame = 0;
+var timer = new AnimationTimerExtend() {
+    handle: function handle(now) {
+        if (frame < 800) {
+            renderFrame();
+            frame++;
+        } else {
+            checkImageAndExit();
+            timer.stop();
+        }
+    }
+};
+timer.start();
Binary file nashorn/test/script/jfx/kaleidoscope/golden/linux.png has changed
Binary file nashorn/test/script/jfx/kaleidoscope/golden/macosx.png has changed
Binary file nashorn/test/script/jfx/kaleidoscope/golden/windows.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/jfx/spread.js	Tue Nov 05 13:09:40 2013 +0400
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Testing JavaFX canvas run by Nashorn.
+ *
+ * @test/nocompare
+ * @run
+ * @fork
+ */
+ 
+TESTNAME = "spread";
+
+var WIDTH = 800;
+var HEIGHT = 600;
+var canvas = new Canvas(WIDTH, HEIGHT);
+var context = canvas.graphicsContext2D;
+
+/* "Spread" tech demo of canvas by Tom Theisen
+ *
+ * This will animate a sequence of branch structures in a canvas element.
+ * Each frame, a new direction is calculated, similar to the last frame.
+ */
+
+var start_width = 20;           // starting width of each branch
+var frame_time = 30;            // milliseconds per frame
+var straighten_factor = 0.95;   // value from 0 to 1, factor applied to direction_offset every frame
+var curviness = 0.2;            // amount of random direction change each frame
+
+var color_speed = 0.03;     // speed at which colors change when cycling is enabled
+var branch_shrink = 0.95;   // factor by which branches shrink every frame
+var min_width = 1;          // minimum WIDTH for branch, after which they are discontinued
+var branch_opacity = 0.4;   // opacity of lines drawn
+var branch_count = 3;       // branch count per tree
+var branch_bud_size = 0.5;  // ratio of original branch size at which branch will split
+var branch_bud_angle = 1;   // angle offset for split branch;
+
+var paper;                  // reference to graphics context
+var branches = Object();    // linked list of active branches
+var color_styles = [];      // pre-computed list of colors as styles. format: (r,g,b,a)    
+var direction_offset = 0;   // current direction offset in radians.  this is applied to all branches.
+var frame = 0;              // frame counter
+var timespent = 0;          // total time spent so far, used to calculate average frame render duration
+var frameratespan;          // html span element for updating performance number
+
+// preferences object, contains an attribute for each user setting
+var prefs = {
+    wrap: true,             // causes branches reaching edge of viewable area to appear on opposite side
+    fade: false,             // fade existing graphics on each frame
+    cycle: true,            // gradually change colors each frame
+    new_branch_frames: 20    // number of frames elapsed between each auto-generated tree
+};
+
+// create tree at the specified position with number of branches
+function create_tree(branches, start_width, position, branch_count) {
+    var angle_offset = Math.PI * 2 / branch_count;
+    for (var i = 0; i < branch_count; ++i) {
+        branch_add(branches, new Branch(position, angle_offset * i, start_width));
+    }
+}
+
+// add branch to collection
+function branch_add(branches, branch) {
+    branch.next = branches.next;
+    branches.next = branch;
+}
+
+// get the coordinates for the position of a new tree
+// use the center of the canvas
+function get_new_tree_center(width, height) {
+    return {
+        x: 0.5 * width, 
+        y: 0.5 * height 
+    };
+}
+
+// Branch constructor
+// position has x and y properties
+// direction is in radians
+function Branch(position, direction, width) {
+    this.x = position.x;
+    this.y = position.y;
+    this.width = width;
+    this.original_width = width;
+    this.direction = direction;
+}
+
+// update position, direction and width of a particular branch
+function branch_update(branches, branch, paper) {
+    paper.beginPath();
+    paper.lineWidth = branch.width;
+    paper.moveTo(branch.x, branch.y);
+    
+    branch.width *= branch_shrink;
+    branch.direction += direction_offset;
+    branch.x += Math.cos(branch.direction) * branch.width;
+    branch.y += Math.sin(branch.direction) * branch.width;
+    
+    paper.lineTo(branch.x, branch.y);
+    paper.stroke();
+    
+    if (prefs.wrap) wrap_branch(branch, WIDTH, HEIGHT);
+
+    if (branch.width < branch.original_width * branch_bud_size) {
+        branch.original_width *= branch_bud_size;
+        branch_add(branches, new Branch(branch, branch.direction + 1, branch.original_width));
+    }
+}
+
+function draw_frame() {
+    if (prefs.fade) {
+        paper.fillRect(0, 0, WIDTH, HEIGHT);
+    }
+
+    if (prefs.cycle) {
+        paper.setStroke(Paint.valueOf(color_styles[frame % color_styles.length]));
+    }
+
+    if (frame++ % prefs.new_branch_frames == 0) {
+        create_tree(branches, start_width, get_new_tree_center(WIDTH, HEIGHT), branch_count);
+    }
+    
+    direction_offset += (0.35 + (frame % 200) * 0.0015) * curviness - curviness / 2;
+    direction_offset *= straighten_factor;
+    
+    var branch = branches;
+    var prev_branch = branches;
+    while (branch = branch.next) {
+        branch_update(branches, branch, paper);
+        
+        if (branch.width < min_width) {
+            // remove branch from list
+            prev_branch.next = branch.next;
+        }
+        
+        prev_branch = branch;
+    }
+}
+
+// constrain branch position to visible area by "wrapping" from edge to edge
+function wrap_branch(branch, WIDTH, HEIGHT) {
+    branch.x = positive_mod(branch.x, WIDTH);
+    branch.y = positive_mod(branch.y, HEIGHT);
+}
+
+// for a < 0, b > 0, javascript returns a negative number for a % b
+// this is a variant of the % operator that adds b to the result in this case
+function positive_mod(a, b) {
+    // ECMA 262 11.5.3: Applying the % Operator 
+    // remainder operator does not convert operands to integers,
+    // although negative results are possible
+
+    return ((a % b) + b) % b;
+}
+
+// pre-compute color styles that will be used for color cycling
+function populate_colors(color_speed, color_styles, branch_opacity) {
+    // used in calculation of RGB values
+    var two_thirds_pi = Math.PI * 2 / 3;
+    var four_thirds_pi = Math.PI * 4 / 3;
+    var two_pi = Math.PI * 2;
+
+    // hue does represent hue, but not in the conventional HSL scheme
+    for(var hue = 0; hue < two_pi; hue += color_speed) {
+        var r = Math.floor(Math.sin(hue) * 128 + 128);
+        var g = Math.floor(Math.sin(hue + two_thirds_pi) * 128 + 128);
+        var b = Math.floor(Math.sin(hue + four_thirds_pi) * 128 + 128);
+        color = "rgba(" + [r, g, b, branch_opacity].join() + ")";
+
+        color_styles.push(color);
+    }
+}
+
+// apply initial settings to canvas object
+function setup_canvas() {
+    paper = canvas.graphicsContext2D;
+    paper.setFill(Paint.valueOf('rgb(0, 0, 0)'));
+    paper.fillRect(0, 0, WIDTH, HEIGHT);
+    paper.setFill(Paint.valueOf("rgba(0, 0, 0, 0.005)"));
+    paper.setStroke(Paint.valueOf("rgba(128, 128, 64, " + String(branch_opacity) + ")"));
+}
+
+populate_colors(color_speed, color_styles, branch_opacity);
+setup_canvas();
+
+var stack = new StackPane();
+var pane = new BorderPane();
+pane.setCenter(canvas);
+stack.getChildren().add(pane);
+$STAGE.scene = new Scene(stack);
+var timer = new AnimationTimerExtend() {
+    handle: function handle(now) {
+        if (frame < 200) {
+		    draw_frame();
+        } else {
+            checkImageAndExit();
+            timer.stop();
+        }
+    }
+};
+timer.start();
+
Binary file nashorn/test/script/jfx/spread/golden/linux.png has changed
Binary file nashorn/test/script/jfx/spread/golden/macosx.png has changed
Binary file nashorn/test/script/jfx/spread/golden/windows.png has changed