APP JavaFX Imaging

Joe

Thành viên VIP
21/1/13
2,969
1,310
113
Hi

Again, it's time to show you how to develop a little app using JavaFX. From Desktop to Web app images are usually the most efficient way to depict a problem or to explain a process. However, image is fixed and sometimes it won't fit into an area (or field) reserved for it. The problem is then: creating a new image or reducing/enlarging an image so that it could fit to your requirement? Java offers you two ways to work with image: SWING and JavaFX.

Today I show you how to develop a JavaFX app that allows you to reduce or enlarge an image. Later I will show you how to do the same work in java SWING. Imaging bases on three tasks:
  1. Displaying: JavaFX (or SWING) as the interactive GUI dialog
  2. Processing: API javax.imageio.ImageIO, API BufferedImage and API java.awt.Graphics2D as the processing bases
  3. Saving: the reduced/enlarged image from the original
As said, the Image-Processing bases on two APIs. For the input (read) and output (write): javax.imageio.ImageIO.
Java:
  BufferedImage bImage = ImageIO.read(new File(imageFileName)); // from a file
  BufferedImage bImage = ImageIO.read(new File(new java.net.URL(linkToImage))); // from a Web-Link
//
javax.imageio.ImageIO.write(bImage, "PNG", new File(fileName)); // create a new Image from bImage
The String "PNG" is the Format String and it converts the BufferedImage into the PNG-format. It's possible to convert any format into another Format defined by the Format String.

For the Reduction/Enlargement: BufferedImage and java.awt.Graphics2D.
Java:
    Image image = SwingFXUtils.toFXImage(bImage, null); // convert BufferedImage to JFX-Image (different to SWING Image)
    double height = image.getHeight();
    double width = image.getWidth();
    // scale is the reduction (less than 1) or enlargement (bigger than 1) factor
    int w = (int)(scale * width);
    int h = (int)(scale * height);
    // create a new BufferedImage
    BufferedImage bImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    java.awt.Graphics2D graphics2D = bImg.createGraphics();
    graphics2D.setBackground(java.awt.Color.WHITE);
    graphics2D.setPaint(java.awt.Color.WHITE);
    graphics2D.fillRect(0, 0, w, h);
    graphics2D.setRenderingHint(java.awt.RenderingHints.KEY_INTERPOLATION,
                                    java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR);

    graphics2D.drawImage(bImage, 0, 0, w, h, null); // reduced or enlarged the original image
    // save the new BufferedImage to a new file
    javax.imageio.ImageIO.write(bImg, sfx, new File(fileName));
As you see, the Image-Processing is a straight-forward application of 3 APIs: javax.imageio.ImageIO, API BufferedImage and API java.awt.Graphics2D

The following JavaFX app shows you how they work:
Java:
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.util.*;
import java.io.*;
// JFX
import javafx.beans.binding.Bindings;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.control.Alert.*;
import javafx.scene.layout.*;
import javafx.stage.*;
// Joe Nartca (C)
public class Imaging extends Application {   

  public void start(final Stage stage) {
    stage.setTitle("Imaging in JavaFX");
    List<String> lst = getParameters().getRaw();
    CSS = lst.size() > 0? lst.get(0):"c:/jfx/image/joe.css";      
    css = (new File(CSS)).exists();
    
    ImageView imgView = new ImageView();  
    imgView.setPreserveRatio(true);
    
    Label lab = new Label("Scaling Ratio: 1.0 (or 100%)");
    
    Slider slider = new Slider(0d, 5.0d, 1d);
    slider.setMajorTickUnit(0.2d);
    slider.setMinorTickCount(1);
    slider.setShowTickMarks(true);
    slider.setShowTickLabels(true);
    slider.setSnapToTicks(true);
    slider.setPrefWidth(900d);
    slider.setValue(1d);
    slider.valueProperty().addListener((observable, oValue, nValue) -> {
      if (width == 0 || height == 0) return;
      scale = nValue.doubleValue();
      lab.setText(String.format("Scaling Ratio: %.2f (or %.2f%%)",nValue, scale*100));
      double old = oValue.doubleValue();
      if (old != scale && scale > 0d && bImage != null) {
        int w = (int)(scale * width);
        int h = (int)(scale * height);
        bImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        java.awt.Graphics2D graphics2D = bImg.createGraphics();
        graphics2D.setBackground(java.awt.Color.WHITE);
        graphics2D.setPaint(java.awt.Color.WHITE);
        graphics2D.fillRect(0, 0, w, h);
        graphics2D.setRenderingHint(java.awt.RenderingHints.KEY_INTERPOLATION,
                                    java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        graphics2D.drawImage(bImage, 0, 0, w, h, null);
        Image img = SwingFXUtils.toFXImage(bImg, null);
        imgView.setImage(img);
      }
    });
    Button DOWN = new Button("DOWNLOAD");
    DOWN.setOnAction(e -> {
      String link = getText("WEB-Download Image", "WebLink:");
      if (link != null && !link.isEmpty()) try { // download the image
        bImage = ImageIO.read(new java.net.URL(link));
        int p = link.lastIndexOf(".");
        sfx = (p < 0)? "jpg":link.substring(p+1);
        slider.setValue(1d);
        setView(imgView);
        fileName = link;
      } catch (Exception ex) {
        alarm("Download problems", ex.toString());
      }
    });
    Button UP = new Button("UPLOAD");
    UP.setOnAction(e -> {
      FileChooser fChooser = new FileChooser();
      fChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
      fChooser.setTitle("JPG/PNG/GIF");
      fChooser.getExtensionFilters().
               add(new FileChooser.ExtensionFilter("Image",
                                                   "*.jpg", "*.JPG",
                                                   "*.png", "*.PNG",
                                                   "*.bmp", "*.BMP",
                                                   "*.gif", "*.GIF"));
      File file = fChooser.showOpenDialog(stage);
      if (file != null) {
        fileName = file.getName();
        try {
          bImage = ImageIO.read(file);
          lab.setText("Scaling Ratio: 1.0 (or 100%)");
          sfx = fileName.substring(fileName.lastIndexOf(".")+1);
          slider.setValue(1d);
          setView(imgView);
        } catch (Exception ex) {
          alarm("Upload problems", ex.toString());
        }
      }
    });
    Button CPY = new Button("SAVE AS");
    CPY.setOnAction(e -> {
      if (scale == 0) return;
      String fName = getText(String.format("Resizing: %.2f (or %.2f%%) of ",scale, scale*100)+fileName,
                             "Save to:");
      if (fName != null && !fName.isEmpty() && bImg != null) try {
        int b = fName.lastIndexOf(".");
        if (b < 0) fName = "."+sfx;
        else {
          String x = fName.substring(b+1);
          if (!x.equalsIgnoreCase(sfx)) sfx = x;
        }
        javax.imageio.ImageIO.write(bImg, sfx, new File(fName));
      } catch (Exception ex) {
        alarm("Resizing problems", ex.toString());
      }
    });
    
    HBox hBox = new HBox(10);
    hBox.setAlignment(Pos.CENTER);
    hBox.getChildren().addAll(DOWN, UP, CPY);
        
    VBox vBox = new VBox( );
    // Insets(double top, double right, double bottom, double left)
    vBox.setPadding(new Insets(10, 5, 20, 5));
    vBox.getChildren().addAll(imgView, lab, slider, hBox);
    vBox.setAlignment(Pos.BOTTOM_CENTER);
    Scene scene = new Scene(vBox, 1000, 610);
    setStyle(UP);
    setStyle(CPY);
    setStyle(DOWN);
    scene.getRoot().setStyle(vStyle);
    stage.setTitle("Imaging");
    stage.setScene(scene);
    stage.show();
    // insert scaled Value into the slider-thumb
    slider.applyCss();
    slider.layout();
    Pane thumb = (Pane) slider.lookup(".thumb");
    Label label = new Label();
    label.textProperty().bind(slider.valueProperty().asString("%.2f"));
    thumb.getChildren().add(label);
  
  }
  // TextDialog-----------------------------------------------------------------
  private String getText(String hTxt, String cTxt) {
    TextInputDialog dia = new TextInputDialog( );
    if (css) { // set our style from joe.css
      DialogPane dp = dia.getDialogPane();
      dp.getStylesheets().add("file:///" + CSS.replace("\\", "/"));
      dp.getStyleClass().add("css");
    }
    dia.setTitle("Resizing Image");
    dia.setHeaderText(hTxt);
    dia.setContentText(cTxt);
    Optional<String> inp = dia.showAndWait();
    if (inp.isPresent())return inp.get().trim();
    return null;
  }
  // set ImageView
  private void setView(ImageView iv) {
    Image image = SwingFXUtils.toFXImage(bImage, null);
    height = image.getHeight();
    width = image.getWidth();
    iv.setImage(image);
    bImg = bImage;
    scale = 1d;
  }
  // alert problems
  private void alarm(String h, String c) {
    Alert alert = new Alert(AlertType.ERROR);
    alert.setTitle("Error");
    alert.setHeaderText(h);
    alert.setContentText(c);
    alert.showAndWait();
  }
  private void setStyle(Button but) {
     but.setStyle(bStyle);
     but.styleProperty().bind(Bindings.when(but
                                      .hoverProperty())
                                      .then(hover)
                                      .otherwise(bStyle));
  }
  private boolean css;
  private String fileName, sfx, CSS;
  private BufferedImage bImage, bImg;
  private double scale, width, height;
  private String hover  = "-fx-background-color: #afeeee;"; // paleturquoise
  private String vStyle = "-fx-background-color: burlywood;-fx-font-size: 11pt;-fx-base: silver;";                         
  private String bStyle = "-fx-background-color: #c3c4c4,"+
                          "   linear-gradient(#d6d6d6 50%, white 100%),"+
                          "   radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%,"+
                          "                   rgba(230,230,230,0) 50%);"+
                          "-fx-background-radius: 30;"+
                          "-fx-background-insets: 0,1,1;"+
                          "-fx-text-fill: black;"+
                          "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.6), 3, 0.0, 0, 1);";
}
[COLOR=rgb(255, 255, 255)]
And the CSS script used by the JFX-TextInput Dialog
Java:
.joe {
    -fx-background-color: darkgray;
}
.joe > *.button-bar > *.container {
   -fx-background-color: silver;
}
.joe .button {
    -fx-background-color:
        linear-gradient(#f0ff35, #a9ff00),
        radial-gradient(center 50% -40%, radius 200%, #b8ee36 45%, #80c800 50%);
    -fx-background-radius: 6, 5;
    -fx-background-insets: 0, 1;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.4) , 5, 0.0 , 0 , 1 );
    -fx-text-fill: #395306;
    -fx-font-size: 11pt;
}
.joe .button:hover {
  -fx-background-color: bisque;
}
.joe .textfield {
  -fx-background-color: lightgray;
}
.joe > *.label.content {
    -fx-font-size: 13px;
    -fx-font-weight: bold;
    -fx-text-fill: blue;
}
.joe:header *.header-panel{
    -fx-background-color: silver;
}
.joe:header *.header-panel *.label {
    -fx-font-size: 13px;
    -fx-font-weight: bold;
    -fx-text-fill: darkblue;
    -fx-fill: #292929;
}
Upload.png

After picking whaleShark.jpg
Upload_1.png

Reduce to 0.4 (scale)Upload_2.png

Convert whaleShart.jpg to whaleShark_04.png with a reduction of 0.4 from the original size
Upload_3.png

and here is the result:
Upload_4.png

Have fun with JavaFX Imaging
Joe