Image Viewer in Java
-------------- ImageViewer.java ----------------
import java.util.*;
import java.io.*;
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.canvas.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.geometry.*;
import javafx.beans.*;
import javafx.embed.swing.SwingFXUtils;
import javax.imageio.ImageIO;
public class ImageViewer extends Application {
final Paint imageBackground = Color.GOLD;
final double scrollScaleDelta = 1.1;
final double keyScaleDelta = 1.2;
final KeyCombination kcZoomIn = new KeyCodeCombination(KeyCode.ADD);
final KeyCombination kcZoomOut = new KeyCodeCombination(KeyCode.SUBTRACT);
final KeyCombination kcZoomToFit = new KeyCodeCombination(KeyCode.MULTIPLY);
final KeyCombination kcActualSize = new KeyCodeCombination(KeyCode.DIVIDE);
final int ACTUAL = 0;
final int SHRINK = 1;
final int ZOOM = 2;
final int imageSize = SHRINK;
File[] listFiles;
int fileId = 0;
double lastX, lastY;
double scale = 1;
ImageView imageView;
ScrollPane scrollPane;
Pane pane;
@Override
public void start(Stage stage) {
BorderPane layout = new BorderPane();
stage.setScene(new Scene(layout, 600, 400));
imageView = new ImageView();
pane = new Pane(imageView);
pane.setBackground(new Background(new BackgroundFill(imageBackground, null, null)));
scrollPane = new ScrollPane(pane);
InvalidationListener il = obs -> update(scale, null);
scrollPane.widthProperty().addListener(il);
scrollPane.heightProperty().addListener(il);
pane.setOnMousePressed(me -> {
lastX = me.getX();
lastY = me.getY();
});
pane.setOnMouseDragged(me -> {
double dw = pane.getWidth() - scrollPane.getViewportBounds().getWidth();
double dh = pane.getHeight() - scrollPane.getViewportBounds().getHeight();
double vw = scrollPane.getHvalue() - (me.getX() - lastX) / dw;
double vh = scrollPane.getVvalue() - (me.getY() - lastY) / dh;
scrollPane.setHvalue(Math.max(0, Math.min(vw, 1)));
scrollPane.setVvalue(Math.max(0, Math.min(vh, 1)));
});
pane.setOnDragDetected(me -> { pane.setCursor(Cursor.CLOSED_HAND); });
pane.addEventFilter(MouseEvent.MOUSE_RELEASED, me -> { pane.setCursor(Cursor.DEFAULT); });
scrollPane.addEventFilter(ScrollEvent.SCROLL, se -> {
if (se.getDeltaY() != 0) {
double sc = (se.getDeltaY() < 0 ? scale * scrollScaleDelta : scale / scrollScaleDelta);
update(sc, se.getPickResult().getIntersectedPoint());
se.consume();
}
});
scrollPane.addEventFilter(KeyEvent.KEY_PRESSED, ke -> {
if (ke.getCode() == KeyCode.PAGE_UP) image(fileId - 1);
else if (ke.getCode() == KeyCode.PAGE_DOWN) image(fileId + 1);
else if (ke.getCode() == KeyCode.HOME) image(0);
else if (ke.getCode() == KeyCode.END) image(listFiles.length - 1);
else if (kcZoomIn.match(ke)) update(scale * keyScaleDelta, null);
else if (kcZoomOut.match(ke)) update(scale / keyScaleDelta, null);
else if (kcZoomToFit.match(ke)) update(scale(SHRINK), null);
else if (kcActualSize.match(ke)) update(scale(ACTUAL), null);
//else if (ke.getCode() == KeyCode.F5) update(scale(ZOOM), null);
else return;
ke.consume();
});
//scrollPane.setStyle("-fx-background: rgb(212,208,200);");
layout.setCenter(scrollPane);
String path = getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
//if (path.endsWith(".jar")) path = path.substring(0, path.lastIndexOf("/") + 1);
listFiles = createListFiles(path);
if (listFiles.length == 0) listFiles = createDemo(path);
stage.show();
scrollPane.requestFocus();
Platform.runLater(() -> { image(0); });
}
void update(double newScale, Point3D point) {
if (imageView.getImage() == null) return;
double newWidth = imageView.getImage().getWidth() * newScale;
double newHeight = imageView.getImage().getHeight() * newScale;
if (Math.min(newWidth, newHeight) < 1 || Math.max(newWidth, newHeight) > 20_000) newScale = scale;
double vx = scrollPane.getHvalue() * (pane.getWidth() - scrollPane.getViewportBounds().getWidth());
double vy = scrollPane.getVvalue() * (pane.getHeight() - scrollPane.getViewportBounds().getHeight());
if (point == null) point = new Point3D(
vx + scrollPane.getViewportBounds().getWidth() / 2,
vy + scrollPane.getViewportBounds().getHeight() / 2, 0);
double k = newScale / scale - 1;
scale = newScale;
imageView.setFitWidth(imageView.getImage().getWidth() * scale);
imageView.setFitHeight(imageView.getImage().getHeight() * scale);
scrollPane.layout();
double dw = pane.getWidth() - scrollPane.getViewportBounds().getWidth();
double dh = pane.getHeight() - scrollPane.getViewportBounds().getHeight();
if (dw > 0) scrollPane.setHvalue(Math.max(0, Math.min((vx + point.getX() * k) / dw, 1)));
if (dh > 0) scrollPane.setVvalue(Math.max(0, Math.min((vy + point.getY() * k) / dh, 1)));
//dw = pane.getWidth() - scrollPane.getWidth() - scrollPane.getPadding().getLeft() - scrollPane.getPadding().getRight();
//dh = pane.getHeight() - scrollPane.getHeight() - scrollPane.getPadding().getTop() - scrollPane.getPadding().getBottom();
pane.setTranslateX(dw >= 0 ? 0 : -dw / 2);
pane.setTranslateY(dh >= 0 ? 0 : -dh / 2);
}
double scale(int v) {
if (v == ACTUAL || imageView.getImage() == null) return 1;
double width = scrollPane.getWidth() - scrollPane.getPadding().getLeft() - scrollPane.getPadding().getRight();
double height = scrollPane.getHeight() - scrollPane.getPadding().getTop() - scrollPane.getPadding().getBottom();
double scaleWidth = width / imageView.getImage().getWidth();
double scaleHeight = height / imageView.getImage().getHeight();
double sc = Math.min(scaleWidth, scaleHeight);
if (v == SHRINK) sc = Math.min(1, sc);
return sc;
}
void image(int newFileId) {
if (listFiles != null && newFileId >= 0 && newFileId < listFiles.length) {
fileId = newFileId;
imageView.setImage(new Image(listFiles[fileId].toURI().toString()));
scrollPane.setHvalue(0);
scrollPane.setVvalue(0);
update(scale(imageSize), Point3D.ZERO);
}
}
File[] createListFiles(String path) {
final String[] extensions = new String[] {".bmp", ".gif", ".jpeg", ".jpg", ".png"};
File[] files = new File(path).listFiles((dir, name) -> {
for (String extension : extensions) {
if (name.toLowerCase().endsWith(extension)) return true;
}
return false;
});
//if (files == null) return new File[0];
Arrays.sort(files, (f1, f2) -> f1.getName().compareToIgnoreCase(f2.getName()));
return files;
}
File[] createDemo(String path) {
final int[][] mm = new int[][] {{3, 3}, {13, 13}, {13, 3}, {3, 13}};
final int sz = 100;
final int s = 10;
SnapshotParameters sp = new SnapshotParameters();
sp.setFill(Color.TRANSPARENT);
Canvas canvas = new Canvas();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setTextBaseline(VPos.CENTER);
gc.setTextAlign(TextAlignment.CENTER);
gc.setFill(Color.web("808080"));
for (int[] m : mm) {
int nx = m[0];
int ny = m[1];
canvas.setWidth(nx * sz + s);
canvas.setHeight(ny * sz + s);
gc.setFont(Font.font((sz - s) * .3));
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (int y = 0; y < ny; y++) {
gc.fillRect(0, y * sz, sz * nx, s);
for (int x = 0; x < nx; x++) {
if (y == 0) gc.fillRect(x * sz, 0, s, sz * ny);
gc.fillText(x + "," + y, x * sz + s + (sz - s) / 2, y * sz + s + (sz - s) / 2);
}
}
gc.fillRect(0, ny * sz, sz * nx + s, s);
gc.fillRect(nx * sz, 0, s, sz * ny);
File file = new File(path, "image" + nx + "x" + ny + ".png");
WritableImage snapshot = canvas.snapshot(sp, null);
try {
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", file);
} catch (Exception e) { e.printStackTrace(); }
}
return createListFiles(path);
}
}