I know how to draw HTML to a Graphics2D object using Swing's limited built-in HTML support (see http://www.java.net/node/680674), but I need better rendering. Most importantly for this particular chemistry diagram drawing application, Swing's HTML support does not extend to nested sub/superscripts. Better CSS support would be nice too. I don't need image embedding or interactive features such as Javascript or hotlinks.
The HTML text is scaled and rotated and then drawn into a diagram that presumably contains additional text and graphics. The Graphics2D target may be the screen, a printer, or (via iText) a PDF file. I doubt that any solution involving conversion via a BufferedImage or the like can be adequately compact when producing PDF files of publication quality.
My (possibly incorrect) impression is that JavaFX 2.0 does not yet have a solution to this, though it might eventually. (If an earlier version can do this, that might be a solution.) Rewriting the entire application from Swing to JavaFX is not realistic.
This application is free and open source, so any tool it uses probably needs to be freely distributable also. Otherwise, I believe JWebEngine might have fit the bill.
Any help would be appreciated.
You could use a JavaFX WebView node - it has very good HTML tag and css support. You can rotate and scale the WebView node using JavaFX primitives. MathJax can be used within WebView to get high quality equation rendering (if just plain html and css alone doesn't do the job for you). Using JavaFX 2.2, you can take a snapshot of the WebView node and render it to a JavaFX image. You can convert that JavaFX image to an awt BufferedImage using JavaFX 2.2 SwingFXUtils and write it out to a file in many formats using ImageIO.
Here is an example of rendering a piechart node to a png. Depending on the complexity of your html, sometimes an high quality image will compress well to (for example) a png file. In the pichart sample, the 2000x2000 pixel piechart with text and colored gradients saved to a png file of 168kb.
Rewriting the entire application from Swing to JavaFX is not necessary as JavaFX includes the JFXPanel for embedding JavaFX applications inside existing Swing applications. The node snapshot step does not even require the node to be rendered to a screen (it can all be done through memory buffers) - though the JavaFX system would probably need to have been initiated and launched in a JavaFX application or a JFXPanel first.
All of the above may or may not end up giving you the result you want, but it seems a promising avenue to examine.
Update
I ran a couple of tests on this and though I can snapshot a WebView displayed on the screen as explained in this post, due to some limitation of JavaFX 2.2, I was unable to snapshot a WebView displayed as part of an offscreen scene. This means that the information in this answer is accurate, but only applies to the portion of the HTML which can be displayed in the WebView on a screen; e.g. the technique will not currently work for large documents whose pixel size exceeds the screen pixel size. For some sample code, see https://forums.oracle.com/forums/thread.jspa?threadID=2456191.
After a lot of searching and scraping several pieces together I found that the only problem I had with the example in the Update oracle forum link above was that the size of the webview was fixed and that my css used in the html (not in JavaFX) needed.
overflow-x: hidden;
overflow-y: hidden;
to hide the last scrollbar.
So I come up with the following snapshot method (application with animation just as example of your application):
package application;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javafx.animation.Animation;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.web.WebView;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class WebViewSnapshot extends Application {
BorderPane rootPane;
TranslateTransition animation;
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(50, 50, 50, 50);
rect.setFill(Color.CORAL);
animation = createAnimation(rect);
Button snapshotButton = new Button("Take snapshot");
Pane pane = new Pane(rect);
pane.setMinSize(600, 150);
rootPane = new BorderPane(pane, null, null, snapshotButton, new Label("This is the main scene"));
snapshotButton.setOnAction(e -> {
// html file being shown in webview
File htmlFile = new File ("generated/template.html");
// the resulting snapshot png file
File aboutFile = new File ("generated/about.png");
generate(htmlFile, aboutFile, 1280, 720);
});
BorderPane.setAlignment(snapshotButton, Pos.CENTER);
BorderPane.setMargin(snapshotButton, new Insets(5));
Scene scene = new Scene(rootPane);
primaryStage.setScene(scene);
primaryStage.show();
}
private TranslateTransition createAnimation(Rectangle rect) {
TranslateTransition animation = new TranslateTransition(Duration.seconds(1), rect);
animation.setByX(400);
animation.setCycleCount(Animation.INDEFINITE);
animation.setAutoReverse(true);
animation.play();
return animation;
}
public void generate(File htmlFile, File outputFile, double width, double height) {
animation.pause();
// rootPane is the root of original scene in an FXML controller you get this when you assign it an id
rootPane.setEffect(new GaussianBlur());
Stage primaryStage = (Stage)rootPane.getScene().getWindow();
// creating separate webview holding same html content as in original scene
WebView webView = new WebView();
// with the size I want the snapshot
webView.setPrefSize(width, height);
AnchorPane snapshotRoot = new AnchorPane(webView);
webView.getEngine().load(htmlFile.toURI().toString());
Stage popupStage = new Stage(StageStyle.TRANSPARENT);
popupStage.initOwner(primaryStage);
popupStage.initModality(Modality.APPLICATION_MODAL);
// this popup doesn't really show anything size = 1x1, it just holds the snapshot-webview
popupStage.setScene(new Scene(snapshotRoot, 1, 1));
// pausing to make sure the webview/picture is completely rendered
PauseTransition pt = new PauseTransition(Duration.seconds(2));
pt.setOnFinished(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
WritableImage image = webView.snapshot(null, null);
// writing a png to outputFile
// writing a JPG like this will result in a pink JPG, see other posts
// if somebody can scrape me simple code to convert it ARGB to RGB or something
String format = "png";
try {
ImageIO.write(SwingFXUtils.fromFXImage(image, null), format, outputFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
rootPane.setEffect(null);
popupStage.hide();
animation.play();
}
}
});
// pausing, after pause onFinished event will take + write snapshot
pt.play();
// GO!
popupStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I have an application that displays a fullscreen AnchorPane containing an ImageView with its fit height/width set to the size of the pane to make the image it contains fullscreen iself.
I've just upgraded my setup to a 3840x2160 display, which has me using Windows 10 built-in scaling factor for comfort. However, this scaling now messes with the application.
I'm setting the pane dimension using this code, which can also cycle through multiple monitors.
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] monitors = e.getScreenDevices();
if ((full.isShowing() && monitors.length > 1) || monitor < 0) {
monitor = (monitor + 1) % monitors.length;
GraphicsDevice device = monitors[monitor];
Rectangle bounds = device.getDefaultConfiguration().getBounds();
full.setX(bounds.x);
full.setY(bounds.y);
full.setWidth(bounds.width);
full.setHeight(bounds.height);
}
The pane dimension reports naturally at 3840x2160. However, Windows apply its scaling to the pane and it ends up at an incorrect dimension. My possible fix for this problem are as follow:
Find a way to retrieve the scaling factor and divide the resolution to fit the screen correctly. This is not ideal: the display resolution will be lower than the resolution of the screen.
Find a way to bypass the scaling, effectively telling Windows to not scale the pane, although scaling the rest of the UI is fine.
Anything else?
EDIT: After running a test with setFullScreen on the stage. I've found out this solution does not completely solve the issue. What happens is exactly what I describe in fix #1: The application does appear fullscreen, however this image is displayed at an inferior resolution, only to be upscaled by windows scaling.
EDIT(2): Here's a code sample that highlights the issue
package com.mycompany.mavenproject1;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = new AnchorPane();
Scene scene = new Scene(root);
Rectangle2D bounds = Screen.getPrimary().getBounds();
System.out.println("Bounds:" + bounds);
stage.setX(bounds.getMinX());
stage.setY(bounds.getMinY());
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
On my 3840x2160 screen, the bounds report Bounds:Rectangle2D [minX = 0.0, minY=0.0, maxX=2560.0, maxY=1400.0, width=2560.0, height=1440.0] The pane is visually of the right dimension, but the pane's resolution is lower (windows upscaling at work). Worse the pane does not even show at the expected position of (0, 0) but at roughly (970, -340)... I suppose the position is also affected by the scaling in a very weird way.
Calling stage.setFullScreen(true) moves the pane at the correct position but the pane resolution is still lower than desired. (Also, there is the problem of the full screen exit hint that cannot be disabled, but that's a different matter).
I am trying to display a gif but if the gif is "too long" it for some reason just starts over instead of displaying the whole animation.
I am currently just using this plain code (for testing without any other code interfering) and it won't work:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Group popup = new Group();
Image image = new Image("https://image.ibb.co/hUMzWU/1.gif");
ImageView view = new ImageView(image);
popup.getChildren().add(view);
Scene dialogScene = new Scene(popup);
primaryStage.setScene(dialogScene);
primaryStage.setTitle("Testing Gif Stuff");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
An example for such an image would be: https://img2.picload.org/image/dlldgogw/7.gif
For me it keeps "resetting" right when his arms enter the picture. Any help is appreciated. Using Java 10. Loading from disk or from internet makes no difference.
Some other gifs that won't work either:https://image.ibb.co/jhhsJ9/ae221412fcd5235a.gif (broken as hell)
https://image.ibb.co/fyhL5p/1664d3a95ec06cfd.gif
https://image.ibb.co/hH4NJ9/0beec1ba838fabd2.gif
File size does not seem to be the main issue because the last gif is relatively small (900kb).
I have a javaFX 8 application that works perfectly well in jre 1.8.0_45 but today a user came to me with a problem. After some investigation i realised that it was related to him having a more recent release of the jre, specifically 1.8.0_60.
Im reading a GIS shapefile and drawing several Paths to a Group (like 30.000 or more) in my version it was a bit slow but it worked fine. In the latest version the image appeared distorted. The paths where drawn out of place and out of scale in chunks.
correct image generated under jre 1.8.0_45
distorted image generated under jre 1.8.0_60
So i decided to make a little test application to separate the problem from anything else i might be doing. In doing so i found out that the problem wasn't only when drawing Paths on Group but also in drawing to a canvas. Also if somehow i managed to redraw the screen the image would appear fine. For example i have a checkbox binded with the visible property of the Group containing the paths so if i set it to false and then true it takes some time drawing the scene but then it appears fine. The test app is very simple if you press a button you generate a canvas with some squares 10px10p if you press the other you generate more squares and thus the rendering glitch appears.
package gisUI;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
public class Path2DTestApplication extends Application {
private static final int WIDTH = 10;
Group content = new Group();
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("JavaFX 1.8.0_60 rendering test");
Button button = new Button("Canvas 100 x 30");
button.setOnAction(a->doGenerateCanvas(100,30));
Button button2 = new Button("Canvas 100 x 400");
button2.setOnAction(a->doGenerateCanvas(100,400));
Button button3 = new Button("Paths 100 x 30");
button3.setOnAction(a->doGeneratePaths(100,30));
VBox vBox = new VBox();
vBox.getChildren().addAll(new HBox(button,button2,button3),content);
Group root = new Group();
root.getChildren().add(vBox);
Scene scene = new Scene(root,80*WIDTH,60*WIDTH);//, 1500, 800);//, Color.White);
stage.setScene(scene);
stage.show();
}
private void doGeneratePaths(int maxX,int maxY) {
Pane paths = new Pane();
content.getChildren().clear();
Platform.runLater(()->{
for(int i = 0;i<maxX;i++){
for(int j=0;j<maxY;j++){
paths.getChildren().add(getPath(i,j));
}
}
content.getChildren().add(paths);
});
}
private void doGenerateCanvas(int maxX,int maxY) {
content.getChildren().clear();
Platform.runLater(()->{
Canvas canvas = new Canvas(maxX*WIDTH, maxY*WIDTH);
GraphicsContext gc = canvas.getGraphicsContext2D();
int counter =0;
for(int i = 0;i<maxX;i++){
for(int j=0;j<maxY;j++){
gc.setFill(Color. rgb(255,(int) (Math.random()*255),191));
double[] xCoords = new double[]{i*WIDTH, (i+1)*WIDTH, (i+1)*WIDTH, i*WIDTH};
double[] yCoords = new double[]{j*WIDTH,(j)*WIDTH,(j+1)*WIDTH,(j+1)*WIDTH};
gc.fillPolygon(xCoords,yCoords,xCoords.length);
counter++;
}
}
System.out.println(counter +" polygons added");
content.getChildren().add(canvas);
});
}
protected Node getPath(int i,int j) {
Path path = new Path();
path.getElements().add(new MoveTo(i*WIDTH, j*WIDTH));
path.getElements().add(new LineTo((i+1)*WIDTH, j*WIDTH));
path.getElements().add(new LineTo((i+1)*WIDTH, (j+1)*WIDTH));
path.getElements().add(new LineTo(i*WIDTH, (j+1)*WIDTH));
path.getElements().add(new LineTo(i*WIDTH, j*WIDTH));
Paint currentColor =Color. rgb(255,(int) (Math.random()*255),191);
path.setFill(currentColor);
path.setStrokeWidth(0.1);
return path;
}
public static void main(String[] args) {
Application.launch(Path2DTestApplication.class, args);
}
}
Test 1: press button "Canvas 100 x 30", 3000 squares are drawn
correctly and fast
Test 2: press button "Canvas 100 x 400", 40000
squares are drawn showing the glitch.
Test 3: press button "Canvas
100 x 400" again, 40000 squares are drawn correctly and fast.
Test 4: press button "Paths 100 x 30", 3000 squares are drawn showing the
glitch.
Test 5: press button "Paths 100 x 30" again, 3000 squares are drawn correctly.
This is my first question to stakoverflow so apologies if i wasn't clear enough.
If anyone knows if this is a jre error or there is some problem with my code i would be grateful. Also any workarounds would be helpful.
Tks!!
I played around with this on my MacBook Pro (OS X 10.9.5). This has a native Retina LCD display at 2880x1800, with an attached Thunderbolt LCD display at 2560x1440. Note that the native pixel resolution is different between these two displays.
When I run the code posted, I had no issues with any of the canvas rendering. When rendering the "Paths" option for the first time, or switching from "canvas" to "paths", I saw rendering issues similar to those you describe but only if the application was displayed on the thunderbolt display. When moving to the Retina display, everything worked fine.
So the problem appears to be hardware related. This is clearly a bug, and you should report it as mentioned in a comment, but as a workaround you can switch to software rendering using the system property -Dprism.order=sw from the command line:
java -Dprism.order=sw gisUI.Path2DTestApplication
This removed all rendering errors on my system. You should be aware that this may impact performance.
I have a question about adjusting contrast, saturation and hue of an image that's loaded to jXImageView from swingx library.
I have the ColorAdjust methods.
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setContrast(0.3);
colorAdjust.setHue(-0.03);
colorAdjust.setBrightness(0.2);
colorAdjust.setSaturation(0.2);
When the user click on the "Enhancement" button, the image should change a bit, but how to do that? Remember: I'm using the jXImageView.
I've increased the contrast already by using this code:
float brightenFactor = 1.5f;
BufferedImage imagem = (BufferedImage) jXImageView2.getImage();
RescaleOp op = new RescaleOp(brightenFactor, 0, null);
imagem = op.filter(imagem, imagem);
jXImageView2.updateUI();
Edit
I tryied:
BufferedImage imagem = (BufferedImage) jXImageView2.getImage();
Image image = SwingFXUtils.toFXImage(imagem, null);//<--ERROR on that line (incompatible types: writable image cannot be converted to Image)
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setContrast(0.3);
colorAdjust.setHue(-0.03);
colorAdjust.setBrightness(0.2);
colorAdjust.setSaturation(0.2);
ImageView imageView = new ImageView(image);//<--ERROR on taht line no suitable constructor for ImageView(java.awt.Image)
imageView.setFitWidth(imagem.getWidth());
imageView.setPreserveRatio(true);
imagem = SwingFXUtils.fromFXImage(imageView.snapshot(null, null), null);
jXImageView2.setImage(imagem);
...but without successful.
Sample solution
Image on the left is the original image.
Image on the right is the adjusted image (which has had the color desaturated to make the image monochrome).
This solution works by:
Converting the Swing/AWT BufferedImage into a JavaFX Image.
Using the JavaFX ColorAdjust effect to modify the image.
A snapshot of the color adjusted image is taken to create a new JavaFX image.
The new JavaFX image is converted back to a new Swing/AWT BufferedImage.
Because the solution mixes two different toolkits, the following considerations were applied when creating it:
Be careful of imports used to ensure that the correct class is being used for a given toolkit call; e.g., both JavaFX and Swing/AWT have Color and Image classes, so it is necessary to ensure that the fully qualified class for a given toolkit is used in the right context - passing a Swing Image directly to a JavaFX API would be wrong and vice-versa.
Be careful of threading rules. Snapshots of JavaFX scenes must be made on the JavaFX application thread. Execution of Swing APIs must be made on the Swing event dispatch thread. Various utilities of the respective toolkits (e.g., SwingUtilities and the JavaFX Platform class) are used to ensure threading constraints of the given toolkits are satisfied.
The JavaFX toolkit must be initialized before it can be used. Normally this is done implicitly when your application extends the JavaFX Application class. However Swing applications do not extend the JavaFX application class. So, perhaps somewhat counter-intuitively and poorly documented, a JFXPanel must be instantiated to initialize the JavaFX toolkit before the toolkit is used.
Notes
This solution is crafted to fit the particular requirements of the question (which is a Swing application which needs to make some color adjustments). If you only wish to adjust image colors from within JavaFX and not use Swing, then more straight-forward solutions exist and are preferred.
Calling System.exit is generally enough to shut the JavaFX toolkit down. The sample application calls Platform.exit to explicitly shut the JavaFX toolkit down, but in this case the explicit call to Platform.exit is probably unnecessary.
This means that the ColorAdjuster in the solution can be used from a Swing program without the Swing program explicitly importing any JavaFX classes (although, internally, the ColorAdjuster will import those classes and the system must meet the normal minimum requirements to run both the Swing and JavaFX toolkits). Reducing mixing of imports to a single toolkit per class where possible is desirable because mixing imports within a single class for a mixed JavaFX/Swing application is a good source of tedious errors, due to potential name clashes and threading related headaches.
ColorAdjuster.java
Image color adjusting utility.
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.SnapshotParameters;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javax.swing.SwingUtilities;
import java.awt.image.BufferedImage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/** Uses JavaFX to adjust the color of an AWT/Swing BufferedImage */
public class ColorAdjuster {
// Instantiation of a JFXPanel is necessary otherwise the JavaFX toolkit is not initialized.
// The JFXPanel doesn't actually need to be used, instantiating it in the constructor is enough to trigger toolkit initialization.
private final JFXPanel fxPanel;
public ColorAdjuster() {
// perhaps this check is not necessary, but I feel a bit more comfortable if it is there.
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalArgumentException(
"A ColorAdjuster must be created on the Swing Event Dispatch thread. " +
"Current thread is " + Thread.currentThread()
);
}
fxPanel = new JFXPanel();
}
/**
* Color adjustments to the buffered image are performed with parameters in the range -1.0 to 1.0
*
* #return a new BufferedImage which has colors adjusted from the original image.
**/
public BufferedImage adjustColor(
BufferedImage originalImage,
double hue,
double saturation,
double brightness,
double contrast
) throws ExecutionException, InterruptedException {
// This task will be executed on the JavaFX thread.
FutureTask<BufferedImage> conversionTask = new FutureTask<>(() -> {
// create a JavaFX color adjust effect.
final ColorAdjust monochrome = new ColorAdjust(0, -1, 0, 0);
// convert the input buffered image to a JavaFX image and load it into a JavaFX ImageView.
final ImageView imageView = new ImageView(
SwingFXUtils.toFXImage(
originalImage, null
)
);
// apply the color adjustment.
imageView.setEffect(monochrome);
// snapshot the color adjusted JavaFX image, convert it back to a Swing buffered image and return it.
SnapshotParameters snapshotParameters = new SnapshotParameters();
snapshotParameters.setFill(Color.TRANSPARENT);
return SwingFXUtils.fromFXImage(
imageView.snapshot(
snapshotParameters,
null
),
null
);
});
Platform.runLater(conversionTask);
return conversionTask.get();
}
}
ColorAdjustingSwingAppUsingJavaFX.java
Test harness:
import javafx.application.Platform;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
public class ColorAdjustingSwingAppUsingJavaFX {
private static void initAndShowGUI() {
try {
// This method is invoked on Swing thread
JFrame frame = new JFrame();
// read the original image from a URL.
URL url = new URL(
IMAGE_LOC
);
BufferedImage originalImage = ImageIO.read(url);
// use JavaFX to convert the original image to monochrome.
ColorAdjuster colorAdjuster = new ColorAdjuster();
BufferedImage monochromeImage = colorAdjuster.adjustColor(
originalImage,
0, -1, 0, 0
);
// add the original image and the converted image to the Swing frame.
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(
new JLabel(
new ImageIcon(originalImage)
)
);
frame.getContentPane().add(
new JLabel(
new ImageIcon(monochromeImage)
)
);
// set a handler to close the application on request.
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
// shutdown the JavaFX runtime.
Platform.exit();
// exit the application.
System.exit(0);
}
});
// display the Swing frame.
frame.pack();
frame.setLocation(400, 300);
frame.setVisible(true);
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(
ColorAdjustingSwingAppUsingJavaFX::initAndShowGUI
);
}
// icon source: http://www.iconarchive.com/artist/aha-soft.html
// icon license: Free for non-commercial use, commercial usage: Not allowed
private static final String IMAGE_LOC =
"http://icons.iconarchive.com/icons/aha-soft/desktop-buffet/128/Pizza-icon.png";
}
You need to convert the BufferedImage to a javafx.scene.image.Image, you can use something like...
Image image = SwingFXUtils.toFXImage(imagem, null);
Then you can apply the ColorAdjust...
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setContrast(0.1);
colorAdjust.setHue(-0.05);
colorAdjust.setBrightness(0.1);
colorAdjust.setSaturation(0.2);
ImageView imageView = new ImageView(image);
imageView.setFitWidth(image.getWidth());
imageView.setPreserveRatio(true);
imageView.setEffect(colorAdjust);
Then convert it back again...
imagem = SwingFXUtils.fromFXImage(imageView.snapshot(null, null), null);
This idea is stolen from jewelsea / SaveAdjustedImage.java. What I don't know is, if the ImageView needs to be realised on the screen first all not...
Updated
Just so you are aware, you are crossing two different UI frameworks, like they say in the films, "don't cross the streams!"
JavaFX has a much more tightly controlled set of requirements then Swing does, this is both a good and bad thing.
What you MUST do, is get the JavaFX code to run within it's event thread. This is more tricky than it sounds (and seems to need to be), for example...
Original | Color adjustments (taken from the JavaDocs example) | Monochrome...
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class Test extends Application {
public static void main(String[] args) {
Application.launch();
}
#Override
public void start(Stage stage) throws Exception {
try {
System.out.println("Load image...");
BufferedImage imagem = ImageIO.read(new File("..."));
Image image = SwingFXUtils.toFXImage(imagem, null);
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setHue(0);
colorAdjust.setSaturation(-1);
colorAdjust.setBrightness(0);
colorAdjust.setContrast(0);
// colorAdjust.setHue(-0.05);
// colorAdjust.setSaturation(0.2);
// colorAdjust.setBrightness(0.1);
// colorAdjust.setContrast(0.1);
ImageView imageView = new ImageView(image);
imageView.setFitWidth(image.getWidth());
imageView.setPreserveRatio(true);
imageView.setEffect(colorAdjust);
System.out.println("Convert and save...");
imagem = SwingFXUtils.fromFXImage(imageView.snapshot(null, null), null);
ImageIO.write(imagem, "png", new File("ColorAdjusted.png"));
} catch (IOException exp) {
exp.printStackTrace();
} finally {
Platform.exit();
}
}
}
The next thing is trying to work out how you would get this to work as a utility class...
I have a JavaFX application (Swing users input might also help) which is Image gallery basically. It watches a folder, and as soon as any Image is added, the Image is displayed on the screen.
Sooner as more Images got added, the application memory consumption only grew bigger. On profiling, I have observed that on adding an Image (size 1.3 MB), memory consumption increased by about 50 MB. The class that holds Image is a simple ImageView that holds an Image.
Does any one have similar experiences? The behavior is same on Windows & Mac
PS: I know any code here will help, but there is nothing much to be shown. As I said there is a List of ImageView which holds Image. List of ImageView is binded to another List say l1. On detecting an Image, image is added to l1 and so is added to the actual list which is displayed on screen
EDIT:
I just tried a sample code. I have observed that, on every Iamge it loads (2.3 MB in this case), there is a memory increase of 12 MB each:
package side;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String... args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
ScrollBar bar = new ScrollBar();
bar.setOrientation(Orientation.VERTICAL);
final VBox box = new VBox();
Group root = new Group();
root.getChildren().addAll(box, bar);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Layout Sample");
primaryStage.show();
for (int ik = 0; ik < 6; ik++) {
System.out.println("1");
ImageView i = new ImageView();
InputStream is = new FileInputStream(new File("C:\\Users\\Jatin\\Documents\\BarcodeNew\\w.png"));
Image im = new Image(is);
i.setImage(im);
box.getChildren().add(i);
is.close();
}
//r.close();
}
}
Found two issues:
I wasn't closing my stream.
From this
Remember that the file size of a PNG image and the memory consumption of an uncompressed image in memory are very different.
On specifying the size of the Image it all works well.