Zooming Images With a Magnifying Glass
- Skill Level Intermediate
- Supported Versions JavaFX 1.3
- Key Features Graphics, Event Handling
- Last Updated August 2009
Have you ever tried to implement a magnifying glass that would obediently follow the mouse cursor as you moved it over your image? Did you find that the display jumped to a new location or that the scale was not preserved? In this technical tip, you learn how easily you can create a magnifying glass by using the JavaFX Script programming language.
Before you proceed with the implementation details, see the Magnifying glass in action in the following applet.
Move the mouse cursor over the image to see how smoothly the magnifying glass moves around. Depending on which browser you are using, rotate the mouse wheel or use the + or - keys to change a magnification factor. The magnification factor varies within a range of 0, 5 and 10. You can see the current value at the right side near the bottom of the magnifying glass.
Click the right mouse button to switch between the better-quality filtering mechanism and a faster filtering mechanism when transforming the source image into a view to be displayed in the magnifying glass.
The graphical scene of this application consists of three nodes, as shown in the following code.
scene: scene = Scene { content: [ bgImageView, glassGroup, desc ] }
Here is the purpose of the nodes:
bgImageView- A node that renders the image and is responsible for catching mouse events.glassGroup- A group of three nodes that moves over the background along with the mouse cursor and behaves like a magnifying glass. ThemagGlassnode provides a view of the selected part of the image with a current magnification factor, a text node displays the current magnification factor, and a circle node displays a glass rim.desc- A text node shown on top of the image.
Note that the original image is loaded only once and is used to render two principal nodes: the bgImageView and the magGlass. Both these nodes are created by using the javafx.scene.image.ImageView class.
bgImageView Node
First, you need to render the original image. The following code declares the bgImageView node.
def bgImageView : ImageView = ImageView {
fitWidth: bind scene.width
fitHeight: bind scene.height
image: image
// only cache if we're scaling
cache: bind factor != 1.0
smooth: true
preserveRatio: true
}
The dimensions of the bgImageView node are bound to the dimensions of the scene. Thus the original image is resized with a preserved ratio to fit the dimensions of the scene.
Next, you need to define functions to be called in response to mouse and keyboard events. The following code declares the onMouseMoved, onKeyPressed, onMouseWheelMoved, and onMouseClicked functions.
def bgImageView : ImageView = ImageView {
...
preserveRatio: true
onMouseMoved: function(me: MouseEvent) {
centerX = me.sceneX;
centerY = me.sceneY;
}
onKeyPressed: function(ke: KeyEvent) {
if (ke.code == KeyCode.VK_EQUALS or ke.code == KeyCode.VK_PLUS) {
adjustMagnification(1.0);
} else if (ke.code == KeyCode.VK_MINUS) {
adjustMagnification(-1.0);
}
}
onMouseWheelMoved: function(me: MouseEvent) {
adjustMagnification(me.wheelRotation);
}
onMouseClicked: function(me: MouseEvent) {
if (me.button != MouseButton.PRIMARY) {
magGlass.smooth = not magGlass.smooth;
}
bgImageView.requestFocus();
}
}
Whenever the mouse cursor moves, the onMouseMoved function is called, which updates the centerX and centerY variables used for positioning the magGlass group. Similarly, when the mouse wheel rotates, the onMouseWheelMoved function is called, which recalculates the magnification factor. The onMouseClicked function reverses the value of the smooth variable in response to a right mouse button click.
As soon as one of the functions changes some variables, the JavaFX runtime immediately takes into account the changes and makes the necessary repaints to update the scene of the application.
magGlass Node
Finally, you need to declare the magGlass node. See the implementation in the following code.
def magGlass: ImageView = ImageView {
image: image
preserveRatio: true
fitWidth: bind GLASS_SIZE
fitHeight: bind GLASS_SIZE
smooth: true
viewport: bind Rectangle2D {
minX: viewportCenterX - viewportSize / 2
minY: viewportCenterY - viewportSize / 2
width: viewportSize
height: viewportSize
}
clip: bind Circle { centerX: GLASS_CENTER centerY: GLASS_CENTER radius: GLASS_CENTER - 5 }
}
To render the magGlass node, part of the original image, which is defined with a viewport, is resized to have the specified fitWidth and fitHeight. A circle clip is then used to cut the selected pixels of the viewport for actual rendering. The smaller the viewport is, the higher the magnification.
The following code defines an inverse proportional relationship between the size of the viewport and the magnification factor, and thus provides the magnification effect.
var viewportSize : Number = bind GLASS_SIZE * factor / magnification;
Related Links
- Sample: ScreenShotZoom
Dmitri Trembovetski
Software Engineer, Sun Microsystems
Irina Fedortsova
Technical Writer, Sun Microsystems