Applying effects and transformations to images
- Buy JavaFX Books from Amazon Book Store
Now that you have learned how to load images, what can you do with them? Well, since
ImageView is an instance of the Node class, your loaded images can receive the same
treatment you would ordinarily provide, shapes, for example. In this recipe, we are going to
extend the example from the previous recipe, Loading and displaying images with ImageView,
to add image manipulation functionalities.
Getting ready
In this recipe, we are going to reach back to some of the concepts learned in previous
chapters to extend the image browser example presented in the previous recipe. We will make
use of JavaFX GUI controls and node effects. If you are not familiar with either of these topics,
please review the recipes from Chapter 3, Transformations, Animations, and Effects, and
Chapter 4, Components and Skinning.
The example presented here extends the image browser from the previous recipe to add
image manipulation capabilities. The new version adds GUI controls to scale, rotate, add
effects, and animate the loaded image.
How to do it…
The code snippet presented next has been abbreviated to concentrate on the more interesting
aspects of the code. You can access the full code listing from ch05/source-code/src/
image/ImageBrowserExtendedDemo.fx.
def w = 800;
def h = 600;
def maxW = w * 0.7;
def maxH = h * 0.7;
var scene:Scene;
def slider = Slider{min:1 max:1.5 value:1}
def imgView:ImageView = ImageView{
preserveRatio:true
fitWidth:bind if((slider.value*maxW) < w)
maxW * slider.value else w
fitHeight:bind if((slider.value*maxH) < h)
maxH * slider.value else h
};
var anim = TranslateTransition{
fromX:0 toX:w – maxW
node:imgView repeatCount:TranslateTransition.INDEFINITE
autoReverse:true
}
var rotateAngle = 0;
… //Address Bar Group and loadImg() function not shown
def footer = Group{
layoutX: 20
layoutY: h – 60
content:HBox {
spacing: 12
content:[
slider,
Button{text:"Rotate" action:function(){
rotateAngle = rotateAngle + 90;
imgView.rotate = rotateAngle;
}}
HBox{spacing:7 content:[
Button{text:"Reflection"
onMouseClicked:function(e){
imgView.effect =
if(imgView.effect == null or
or not (imgView.effect instanceof
Reflection))
Reflection{fraction:0.3 topOffset:0}
else null
}}
... // Other effects omitted
Button{text:"Sepia"
onMouseClicked:function(e){
imgView.effect = if(imgView.effect == null
or not (imgView.effect instanceof
SepiaTone)
)
SepiaTone{level:0.7}
else null
}}
Button{text:"Animate"
onMouseClicked:function(e){
if(not anim.running){
anim.play();
}else{
anim.stop();
}
}}
]}
]
}
}
When the ImageView, the Slider, and the other GUI controls are added to stage, and the
application is executed, it will look like what is shown in the next screenshot. In it, you can
see the refl ection effect applied to the image.

How it works…
In the recipe Loading and displaying images with ImageView we have seen how to use the
Image API to load and display local or remote images. This recipe extends the code in that
recipe to not only load the image, but also apply effects and animations to it.
As shown in the previous screenshot, this version of the image browser includes a row of GUI
controls at the bottom of the screen that are used to apply different transformations and
effects to the loaded image. Let’s take a closer look at how the code works:
- Scaling the image— using an instance of the S lider control you can dynamically
grow or shrink the image. To do this, we bind the properties ImageView.fitWidth
and ImageView.fitHeight to Slider.value. This causes the size of the image
to grow or shrink dynamically, while maintaining proper image aspect ratio. The
bound expression includes logic to ensure that the image does not grow excessively
large when it is scaled up as shown below:
ImageView{
fitWidth:bind if((slider.value*maxW) < w)
maxW * slider.value else w
fitHeight:bind if((slider.value*maxH) < h)
maxH * slider.value else h
};
instance by 90 degrees with each click by setting the imgView.rotate property.
respective names. These buttons apply the Reflection, Glow, GaussianBlur ,
Lighting (using a PointLight effect), and SepiaTone effect s to the image
(only Refl ection and Sepia are listed in the code). All buttons work in the same way:
if the effect currently applied to the image is null or the effect is not of the desired
type, then apply the desired effect, otherwise, if the effect is already being applied,
turn it off. This makes the button toggle between its assigned effect.
instance assigned to the variable anim. The transition animation moves the image
from side-to-side indefinitely until the button is pressed again to stop the animation.
See also
- Chapter 3—Transformation, animations, and effects
- Chapter 4—Components and skinning
- Loading and displaying images with ImageView
Creating image effects with blending
In the previous recipe, we saw how easy it is to build an application that loads, displays,
and applies effects to images. In this recipe, we are going to explore how to create new
visual effects by blending two separate image sources.
Getting ready
For this recipe, you will need to be familiar with the concepts of loading and displaying images
in your application using the Image API. If necessary, review the recipe Loading and displaying
images with ImageView. Part of the code also uses transition animation to slide the images one
on top of the other. If you need to review topics regarding animation, refer to the recipe Creating
simple animation with the Transition API from Chapter 3, Transformations, Animations, and
Effects. Lastly, the recipe makes use of GUI controls to capture image URLs and action buttons
to apply the effects. If you are not familiar with JavaFX‘s GUI controls, review the recipe Creating
a form with JavaFX controls from Chapter 4, Components and Skinning.
How to do it…
The code listing given next is abbreviated to show the essential portions that drive the
application. You can get the full listing of this code from ch05/source-code/src/image/
ImageBlendDemo.fx.
var scene:Scene;
def w = 800; def h = 600;
def maxW = w * 0.4; def maxH = h * 0.5;
def img1 = ImageView{
translateX:10 translateY:10
preserveRatio:true
fitWidth:maxW fitHeight:maxH
}
def img2 = ImageView{
translateX:w – maxW translateY:10
preserveRatio:true
fitWidth:maxW fitHeight:maxH
}
def imgPanel = Group {content:[img1, img2]}
def anim = Timeline {
keyFrames: [
KeyFrame{time:1s
values: [
img1.translateX => (w - img1.fitWidth)/2
]
}
KeyFrame{time:1s
values: [
img2.translateX => (w - img2.fitWidth)/2
]
}
]
}
// fn to load img
function loadImg(view:ImageView,url:String){
view.effect = null;
view.image = Image{
backgroundLoading:true
url:url
}
}
// controls bottom of screen
def toggleGrp = ToggleGroup{}
def controls = Group{
layoutY: h – 200
content:[
VBox{width:w spacing:12
hpos:HPos.CENTER nodeHPos:HPos.CENTER content:[
TextBox{id:"addr1" columns:60 promptText:"http://"
action:function(){
loadImg(img1,
(scene.lookup("addr1") as TextBox).text)
}}
TextBox{id:"addr2" columns:60 promptText:"http://"
action:function(){
loadImg(img2,
(scene.lookup("addr2") as TextBox).text)
}}
HBox{
content:[
RadioButton{text:"ADD"
toggleGroup:toggleGrp selected:true
}
... // other blending modes omitted
RadioButton{text:"LIGHTEN"
toggleGroup:toggleGrp
}
]
}
HBox{
content:[
RadioButton{text:"MULTIPLY"
toggleGroup:toggleGrp
}
... //other blending modes omitted
RadioButton{text:"SRC_OVER"
toggleGroup:toggleGrp
}
]
}
Button{
text:”Blend Images”
font:Font.font(“Sans Serif”,
FontWeight.BOLD, 18)
effect:DropShadow{offsetX:3 offsetY:3}
onMouseClicked:function(e){
def mode = toggleGrp.selectedButton.text;
imgPanel.blendMode = BlendMode.valueOf(mode);
anim.rate = 1.0;
anim.playFromStart();
}
onMouseReleased:function(e){
anim.rate = -1.0;
anim.play();
}
}
]}
]
}
When the Group instances imgPanel and controls are placed on the stage, and the
application is executed, it produces the next screenshot. The application lets users enter the
URLs of two images and select a blend mode. When the Blend Images button is pressed, the
images slide to overlap each other and apply the blend effect:

How it works…
The Group class (a node itself) allows the grouping of two or more nodes to be placed on the
scene graph. One of the features of the Group node is its ability to apply a blending algorithm
to the group’s members. It applies its algorithm to all children in its content property when a
blend mode is provided through the blendMode:BlendMode property.
In the previous sample code provided, we use Group instance imgPanel to apply blending
effects to two images placed in the group. Let’s take a closer look at how the application works:
- The images—the first thing we do in the code is to declare two instances of
ImageView, img1 and img2. To ensure that the images fit in a pre-determined
dimension on the screen, we set the properties fitWidth and fitHeight on the
two instances. Then, we place the two images in a Group instance called imgPanel,
where they will receive blending effects. - The image animation— to make things a little interesting, the code uses an instance
of Timeline to animate the two images. The first KeyFrame instance slides img1
from the left-hand side to the middle of the screen, and the second KeyFrame
instance slides img2 from the right-hand side to the middle of the screen. The two
images stack up in the middle of the screen where you can see the selected blending
effect applied. - Loading the images—when the user types the URL location of the images in the
TextBox instances, with property id=”addr1″ and id=”addr2″, and presses
Enter, this invokes the function loadImg(). That function loads and attaches the
loaded image to instances of ImageView img1 and img2, respectively. - Applying the blend—Group variable controls contains two rows of RadioButton
instances (not all shown in previous code). For each instance of RadioButton, the
code assigns the name of a BlendMode as its text content (that is, “ADD”, “COLOR_
BURN”, “MULTIPLY”, and so on). When the user clicks on the button titled Blend
Image, it creates a BlendMode object using the text of the selected radio button,
and applies it to the imgPanel Group containing the images, as shown:
def mode = toggleGrp.selectedButton.text;
imgPanel.blendMode = BlendMode.valueOf(mode);
BlendMode.valueOf(:String) returns an instance of BlendMode based on
a String.
There’s more…
JavaFX supports a multitude of blending options. The following table shows a list of the more
interesting modes:

The BlendedMode class offers more blended modes, including RED, GREEN, BLUE, COLOR_
DOGE, HARD_LIGHT, SOFT_LIGHT, SRC_ATOP, SRC_IN, SRC_OUT, and SRC_OVER.
See also
- Chapter 3—Creating simple animation with the transition API
- Chapter 4—Creating a form with JavaFX controls
- Loading and displaying images with ImageView






September 6, 2010
Java