package effectsplayground;
import javafx.animation.*;
import javafx.animation.transition.*;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.stage.*;
import javafx.reflect.*;
import effectsplayground.EffectControls.*;
import effectsplayground.control.*;
def width = (6 * (82 + 10)) + 20;
def canvasWidth = width-10;
def canvasHeight = 275;
var stage:Stage;
var inBrowser = "true".equals(FX.getArgument("isApplet") as String);
var dragTextVisible =
bind inBrowser and AppletStageExtension.appletDragSupported;
var closeButtonVisible = bind (not inBrowser);
var selectedPreview:Preview = null on replace {
if (selectedPreview == null) {
hideControls();
}
};
var fgUrl = "{__DIR__}images/flower.jpg";
var bgUrl = "{__DIR__}images/water.jpg";
package var fgImage = bind Image {
url: fgUrl
width: canvasWidth * 0.9
height: canvasHeight * 0.9
preserveRatio: true
};
package var bgImage = bind Image {
url: bgUrl
width: canvasWidth * 0.9
height: canvasHeight * 0.9
preserveRatio: true
};
var fgImageView = ImageView {
smooth: true
image: bind fgImage
};
var canvasImage = bind if (selectedPreview != null) selectedPreview.control.canvasImage else fgImageView;
def twidth = 82;
def theight = 71;
package var fgThumb = bind Image {
url: fgUrl
width: twidth
height: theight
preserveRatio: true
};
package var bgThumb = bind Image {
url: bgUrl
width: twidth
height: theight
preserveRatio: true
};
var firstRowControlsVisible = false;
var secondRowControlsVisible = false;
var rotateKnob:CustomKnob;
class Canvas extends CustomNode {
override protected function create() : Node {
Group {
clip: Rectangle { smooth: false width: canvasWidth height: canvasHeight + 1 }
content: [
Rectangle {
width: canvasWidth
height: canvasHeight
fill: LinearGradient {
startX: 0
startY: 0
endX: 0
endY: 1
stops: [
Stop { offset: 0.1 color: Color.BLACK },
Stop { offset: 1.0 color: Color.rgb(193, 193, 193) },
]
}
},
Group {
cache: true
translateX: bind (canvasWidth - canvasImage.layoutBounds.width) / 2 -
canvasImage.layoutBounds.minX
translateY: bind (canvasHeight / 2) - (fgImage.height / 2)
content: bind canvasImage
rotate: bind (360.0 - rotateKnob.value)
},
Rectangle {
width: canvasWidth
height: 2
fill: Color.rgb(103, 103, 103)
},
Rectangle {
y: canvasHeight
width: canvasWidth
height: 1
fill: Color.rgb(240, 240, 240)
}
]
onMouseClicked: function(e) {
hideControls();
}
}
}
}
class ControlPlate extends CustomNode {
public var controlGroup:Node;
override protected function create() : Node {
Group {
content: [
Rectangle {
width: width - 10
height: 56
fill: LinearGradient {
startX: 0
startY: 0
endX: 0
endY: 1
stops: [
Stop { offset: 0.0 color: Color.rgb(102, 102, 102) },
Stop { offset: 0.5 color: Color.rgb(148, 148, 148) },
Stop { offset: 1.0 color: Color.rgb(102, 102, 102) },
]
}
},
Group {
content: bind controlGroup
}
]
}
}
};
class OpenFileFilter extends javax.swing.filechooser.FileFilter {
override public function getDescription() : String {
return "JPG and PNG images";
}
override public function accept(f: java.io.File) : Boolean {
return f.isDirectory()
or f.getName().endsWith(".png")
or f.getName().endsWith(".jpg");
}
}
class SaveFileFilter extends javax.swing.filechooser.FileFilter {
override public function getDescription() : String {
return "PNG images";
}
override public function accept(f: java.io.File) : Boolean {
return f.isDirectory() or f.getName().endsWith(".png");
}
}
class Footer extends CustomNode {
var fileChooser:javax.swing.JFileChooser;
function getFileChooser(open:Boolean) : javax.swing.JFileChooser {
if (fileChooser == null) {
fileChooser = javax.swing.JFileChooser {};
}
var filter = if (open) OpenFileFilter {} else SaveFileFilter {};
fileChooser.setFileFilter(filter);
return fileChooser;
}
override protected function create() : Node {
var xoff = 28;
var yoff = 9;
var xspc = 10;
Group {
content: [
Path {
elements: [
MoveTo { x: 0 y: 0 },
LineTo { x: width y: 0 },
LineTo { x: width y: 27 },
QuadCurveTo { x: width - 3 y: 33 controlX: width controlY: 33 },
QuadCurveTo { x: 3 y: 33 controlX: width / 2, controlY: 100 },
QuadCurveTo { x: 0 y: 30 controlX: 0, controlY: 33 },
]
stroke: null
fill: LinearGradient {
startX: 0
startY: 0
endX: 0
endY: 1
stops: [
Stop { offset: 0.0 color: Color.rgb( 8, 8, 8) },
Stop { offset: 0.25 color: Color.rgb(17, 17, 17) },
Stop { offset: 0.28 color: Color.rgb(17, 17, 17) },
Stop { offset: 0.5 color: Color.rgb(10, 10, 10) }
]
}
},
Button {
translateX: xoff
translateY: yoff
text: "Open Image 1"
action: function() {
var fc = getFileChooser(true);
if (fc.showOpenDialog(null) == javax.swing.JFileChooser.APPROVE_OPTION) {
fgUrl = fc.getSelectedFile().toURI().toString();
}
}
},
Button {
translateX: xoff + Button.width + xspc
translateY: 10
text: "Open Image 2"
action: function() {
var fc = getFileChooser(true);
if (fc.showOpenDialog(null) == javax.swing.JFileChooser.APPROVE_OPTION) {
bgUrl = fc.getSelectedFile().toURI().toString();
}
}
},
Button {
translateX: width - xoff - (2*Button.width) - xspc
translateY: 10
text: "Save Image"
action: function() {
var fc = getFileChooser(false);
fc.setSelectedFile(new java.io.File("funpix.png"));
if (fc.showSaveDialog(null) == javax.swing.JFileChooser.APPROVE_OPTION) {
saveImage(canvasImage, fc.getSelectedFile());
}
}
},
Button {
translateX: width - xoff - Button.width
translateY: 10
text: "Remove Effect"
action: function() {
selectedPreview = null;
hideControls();
}
},
rotateKnob = CustomKnob {
translateX: width/2
translateY: 30
minimum: 0.0
maximum: 360.0
value: 0.0
minAngle: 0.0
maxAngle: 360.0
},
Rectangle {
fill: Color.rgb(40, 40, 40)
width: width
height: 1
}
]
}
}
}
package class Preview extends CustomNode {
public-init var label:String = null;
public-init var control:EffectControl = null on replace {
control.previewNode = this;
};
public-init var firstRow = true;
var selected = bind isSameObject(this, selectedPreview);
var buttonVisible = false;
var buttonOpacity = 0.0;
def selColor = Color.WHITE;
var topColor = Color.color(0.3, 0.3, 0.3);
def colorFade:Timeline = Timeline {
keyFrames: [
KeyFrame {
time: 0s
values: [
topColor => Color.color(0.3, 0.3, 0.3)
buttonOpacity => 0.0
]
action: function() {
buttonVisible = (colorFade.rate > 0);
}
},
KeyFrame {
time: 0.2s
values: [
topColor => Color.WHITE
buttonOpacity => 1.0
]
}
]
}
override protected function create() : Node {
var rect:Rectangle;
var text:Text;
Group {
blocksMouse: true
cursor: Cursor.HAND
content: [
Group {
clip: Rectangle { smooth: false width: twidth height: theight }
cache: true
content: [
Group {
translateX: bind (twidth - control.thumbImage.layoutBounds.width) / 2 -
control.thumbImage.layoutBounds.minX
translateY: 0
content: bind control.thumbImage
}
rect = Rectangle {
y: theight * 0.72
width: twidth
height: theight * 0.28
fill: Color.color(0, 0, 0, 0.7)
stroke: Color.BLACK
},
text = Text {
translateX: bind (twidth - text.layoutBounds.width) / 2 - text.layoutBounds.minX
translateY: bind rect.layoutBounds.minY + (rect.layoutBounds.height / 2) + 4
font: Font { size: 10 }
content: label
fill: Color.WHITE
},
]
},
Rectangle {
width: twidth
height: theight
fill: Color.TRANSPARENT
stroke: bind if (selected) selColor else topColor
},
Polygon {
visible: bind selected
translateX: twidth / 2
translateY: -7
points: [0, 0, 5, 5, -5, 5]
fill: Color.rgb(190, 190, 190)
},
Polygon {
visible: bind selected
translateX: twidth / 2
translateY: theight + 3
points: [-5, 0, 5, 0, 0, 5]
fill: Color.rgb(190, 190, 190)
},
]
onMouseEntered: function(e) {
colorFade.rate = 1;
colorFade.play();
}
onMouseExited: function(e) {
colorFade.rate = -1;
colorFade.play();
}
onMouseClicked: function(e) {
if (firstRow) {
if (not isSameObject(selectedPreview, this)) {
selectedPreview = this;
showFirstRowControls();
} else {
toggleFirstRowControls();
}
} else {
if (not isSameObject(selectedPreview, this)) {
selectedPreview = this;
showSecondRowControls();
} else {
toggleSecondRowControls();
}
}
}
}
}
}
function saveImage(node:Node, file:java.io.File) {
var context = FXLocal.getContext();
var nodeClass = context.findClass("javafx.scene.Node");
var getFXNode = nodeClass.getFunction("impl_getFXNode");
var sgNode = (getFXNode.invoke(context.mirrorOf(node)) as FXLocal.ObjectValue).asObject();
var getBounds = sgNode.getClass().getMethod("getBounds");
var bounds = getBounds.invoke(sgNode) as java.awt.geom.Rectangle2D;
var g2dClass = (context.findClass("java.awt.Graphics2D") as FXLocal.ClassType).getJavaImplementationClass();
var paintMethod = sgNode.getClass().getMethod("render", g2dClass);
var w = bounds.getWidth() as Integer;
var h = bounds.getHeight() as Integer;
var img = new java.awt.image.BufferedImage(w, h,
java.awt.image.BufferedImage.TYPE_INT_ARGB);
var g2 = img.createGraphics();
g2.translate(-bounds.getX(), -bounds.getY());
paintMethod.invoke(sgNode, g2);
g2.dispose();
var savefile;
if (not file.getName().toLowerCase().endsWith(".png")) {
savefile = new java.io.File(file.getParent(), "{file.getName()}.png");
} else {
savefile = file;
}
javax.imageio.ImageIO.write(img, "png", savefile);
}
package var canvas:Canvas = Canvas {
translateX: 5
translateY: 25
};
var firstRowStartY = bind canvas.boundsInParent.maxY;
var secondRowStartY = bind canvas.boundsInParent.maxY + theight + 22;
var footerStartY = bind canvas.boundsInParent.maxY + (theight * 2) + 41;
var firstRowPreviews = HBox {
translateX: 10
translateY: 10
spacing: 9
content: [
Preview {
label: "Blend"
control: BlendControl { }
},
Preview {
label: "Blur"
control: BlurControl { }
},
Preview {
label: "Motion Blur"
control: MotionBlurControl { }
},
Preview {
label: "Bloom"
control: BloomControl { }
},
Preview {
label: "Glow"
control: GlowControl { }
},
Preview {
label: "Color Adjust"
control: ColorAdjustControl { }
},
]
};
var secondRowPreviews = HBox {
translateX: 10
translateY: 10
spacing: 9
content: [
Preview {
label: "Drop Shadow"
control: DropShadowControl { }
firstRow: false
},
Preview {
label: "Inner Shadow"
control: InnerShadowControl { }
firstRow: false
},
Preview {
label: "Perspective"
control: PerspectiveControl { }
firstRow: false
},
Preview {
label: "Lighting"
control: LightingControl { }
firstRow: false
},
Preview {
label: "Sepia Tone"
control: SepiaControl { }
firstRow: false
},
Preview {
label: "Reflection"
control: ReflectionControl { }
firstRow: false
},
]
};
var firstRow = Group {
content: [
Group {
translateX: 0
translateY: bind firstRowStartY
content: [
Rectangle {
width: width
height: theight + 21
fill: LinearGradient {
startX: 0
startY: 0
endX: 0
endY: 1
stops: [
Stop { offset: 0.0 color: Color.rgb(107, 107, 107) },
Stop { offset: 0.95 color: Color.BLACK },
]
}
},
firstRowPreviews,
]
}
]
};
var secondRow = Group {
content: [
Group {
translateX: 0
translateY: bind secondRowStartY
content: [
Rectangle {
width: width
height: theight + 21
fill: LinearGradient {
startX: 0
startY: 0
endX: 0
endY: 1
stops: [
Stop { offset: 0.0 color: Color.rgb(107, 107, 107) },
Stop { offset: 0.95 color: Color.BLACK },
]
}
},
Rectangle {
x: 0
y: -1
width: width
height: 1
fill: Color.rgb(177, 177, 177)
},
Rectangle {
x: 0
y: 0
width: width
height: 1
fill: Color.rgb(80, 80, 80)
},
secondRowPreviews,
]
}
]
};
var footer = Footer {
translateY: bind footerStartY
};
var firstRowSlide = TranslateTransition {
node: firstRow
fromY: 0
toY: -55
duration: 200ms
interpolate: Interpolator.EASEBOTH
};
var secondRowSlide = TranslateTransition {
node: secondRow
fromY: 0
toY: -55
duration: 200ms
interpolate: Interpolator.EASEBOTH
};
var firstRowExpanded = false;
var secondRowExpanded = false;
function expandFirstRow() {
firstRowSlide.rate = 1;
firstRowSlide.play();
firstRowExpanded = true;
}
function collapseFirstRow() {
firstRowSlide.rate = -1;
firstRowSlide.play();
firstRowExpanded = false;
}
function expandSecondRow() {
secondRowSlide.rate = 1;
secondRowSlide.play();
secondRowExpanded = true;
}
function collapseSecondRow() {
secondRowSlide.rate = -1;
secondRowSlide.play();
secondRowExpanded = false;
}
function showFirstRowControls() {
if (not firstRowExpanded) {
expandFirstRow();
}
if (secondRowExpanded) {
collapseSecondRow();
}
firstRowControlsVisible = true;
secondRowControlsVisible = false;
}
function showSecondRowControls() {
if (not firstRowExpanded) {
expandFirstRow();
}
if (not secondRowExpanded) {
expandSecondRow();
}
firstRowControlsVisible = false;
secondRowControlsVisible = true;
}
function hideControls() {
if (firstRowExpanded) {
collapseFirstRow();
}
if (secondRowExpanded) {
collapseSecondRow();
}
firstRowControlsVisible = false;
secondRowControlsVisible = false;
}
function toggleFirstRowControls() {
if (firstRowControlsVisible) {
hideControls();
} else {
showFirstRowControls();
}
}
function toggleSecondRowControls() {
if (secondRowControlsVisible) {
hideControls();
} else {
showSecondRowControls();
}
}
var firstControlPlate = ControlPlate {
translateX: 5
translateY: bind secondRowStartY - 56
controlGroup: bind if (selectedPreview.firstRow) selectedPreview.control.controlsGroup else null
};
var secondControlPlate = ControlPlate {
translateX: 5
translateY: bind footerStartY - 56
controlGroup: bind if (not selectedPreview.firstRow) selectedPreview.control.controlsGroup else null
};
var stageX = 200.0;
var stageY = 200.0;
var detachButton:DetachButton;
var dragRect:Rectangle;
var dragText:Text;
var dragTextOpacity = 0.0;
var dragTextFader = Timeline {
keyFrames: [
at (0s) {
dragTextOpacity => 0.0
},
at (0.2s) {
dragTextOpacity => 1.0
}
]
};
var topPanel:Group = Group {
content: [
Path {
elements: [
MoveTo { x: 3 y: 0 },
LineTo { x: width-3 y: 0 },
QuadCurveTo { x: width y: 3 controlX: width controlY: 0 },
LineTo { x: width y: bind firstRow.layoutBounds.minY },
LineTo { x: 0 y: bind firstRow.layoutBounds.minY },
LineTo { x: 0 y: 3 },
QuadCurveTo { x: 3 y: 0 controlX: 0 controlY: 0 }
]
stroke: null
fill: LinearGradient {
startX: 0
startY: 0
endX: 0
endY: 1
stops: [
Stop { offset: 0.0 color: Color.rgb(126, 127, 134) },
Stop { offset: 0.01 color: Color.rgb( 26, 26, 26) },
Stop { offset: 0.65 color: Color.BLACK },
Stop { offset: 1.0 color: Color.rgb(107, 107, 107) },
]
}
},
dragRect = Rectangle {
visible: bind dragTextVisible or (not inBrowser)
cursor: Cursor.HAND
width: width-40
height: 20
fill: Color.TRANSPARENT
onMouseEntered: function(e) {
dragTextFader.rate = 1;
dragTextFader.play();
}
onMouseExited: function(e) {
dragTextFader.rate = -1;
dragTextFader.play();
}
onMouseDragged: function(e) {
stageX += e.dragX;
stageY += e.dragY;
}
},
CloseButton {
visible: bind closeButtonVisible
translateX: width - 22
translateY: 9
onMouseClicked: function(e) {
stage.close();
}
},
dragText = Text {
visible: bind dragTextVisible
opacity: bind dragTextOpacity
translateX: bind (width - dragText.layoutBounds.width) / 2
translateY: 18
content: "You can drag me out of the browser"
font: Font { size: 10 }
fill: Color.WHITE
},
detachButton = DetachButton {
visible: bind dragTextVisible
translateX: width - 22
translateY: 9
},
firstControlPlate,
secondControlPlate,
canvas,
firstRow,
secondRow,
]
};
function run() {
stage = Stage {
title: "Effects Playground"
x: bind stageX;
y: bind stageY;
resizable: false
style: StageStyle.TRANSPARENT
scene: Scene {
fill: bind if (inBrowser) Color.WHITE else Color.TRANSPARENT
content: [
topPanel,
footer
]
}
extensions: [
AppletStageExtension {
shouldDragStart: function(e): Boolean {
return inBrowser and e.primaryButtonDown and (dragRect.hover or detachButton.hover);
}
onDragStarted: function() {
inBrowser = false;
}
onAppletRestored: function() {
inBrowser = true;
}
useDefaultClose: false
}
]
}
}