here's the problem: I have several images and would like to use them when displaying a HTML in JavaFX's WebView.
Current implementation is very obvious: there is a file, which is linked to in the HTML content. I assume that WebView does not regress from JEditorPane and will only do a single I/O operation even if the image is referenced 10 000 times throughout the content.
However, it would be great to have a single Image instance and feed it to WebView when it encounters the relevant <img> tag.
I have seen that there is a great half-solution involving URL handling, but the problem remains: you have an Image instance that you convert to a storage format (BMP, PNG with proprietary extensions, etc) and keep that in memory. However, this means that each time WebView desires an image resolution, it must then manually parse the image from binary data. In the end, you just have a file mapped to memory plus an internal Image instance instead of a shared Image instance.
With JEditorPane, you could push Images to its image cache and get rid of such problems. Unfortunately, since Java 7, that component is unusable and is out of question.
Basically, is there any chance WebView/WebEngine maintains such a cache/equivalent and is there a way to pre-populate it?
/**
* Encodes the image as a whole into PNG, then into Base64 and finally into an URI suitable for the HTML {#code <img>} tag.
*
* #param image an image
* #return image as URI (image within the URI)
* #throws IIOException if there is a fault with an image writer
* #throws IOException in case of a general I/O error
*/
public static final String getImageSrcForWebEngine(RenderedImage image) throws IIOException, IOException
{
final ByteArrayOutputStream output = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", output);
return "data:base64," + Base64.getMimeEncoder().encodeToString(output.toByteArray());
}
Example of usage:
RenderedImage image = […];
String tag = "<img src=\"" + getImageSrcForWebEngine(image) + "\" border=\"0\" />";
SSCCE:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewWithMemoryImages extends Application
{
private static String IMAGE_IN_MEMORY;
#Override
public void start(Stage primaryStage)
{
WebView webView = new WebView();
webView.getEngine().loadContent("<html><body><img src=\"" + IMAGE_IN_MEMORY + "\"></body></html>");
primaryStage.setScene(new Scene(webView, 420, 420));
primaryStage.show();
}
public static void main(String[] args) throws Exception
{
BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_BGR);
Graphics2D g = image.createGraphics();
try
{
g.setColor(Color.RED);
g.fillRect(0, 0, 400, 400);
g.setColor(Color.WHITE);
g.fillRect(50, 50, 300, 300);
g.setColor(Color.BLACK);
g.fillRect(100, 100, 200, 200);
g.drawString("No image files were used in this WebView.", 90, 70);
}
finally
{
g.dispose();
}
IMAGE_IN_MEMORY = getImageSrcForWebEngine(image);
launch(args);
}
public static String getImageSrcForWebEngine(RenderedImage image) throws IIOException, IOException
{
final ByteArrayOutputStream output = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", output);
return "data:base64," + Base64.getMimeEncoder().encodeToString(output.toByteArray());
}
}
Related
like this blank image is saving
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.effect.Light.Point;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class JavaFxSelectPlay extends Application {
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
WebView wv = new WebView();
WebEngine Img = wv.getEngine();
Img.load("https://app.leadlock.pro/upload/69/1019/images/welcome.jpeg");
final Rectangle selection = new Rectangle();
final Point anchor = new Point();
wv.setOnMousePressed(event -> {
anchor.setX(event.getX());
anchor.setY(event.getY());
selection.setX(event.getX());
selection.setY(event.getY());
selection.setFill(null); // transparent
selection.setStroke(Color.BLACK); // border
selection.getStrokeDashArray().add(10.0);
root.getChildren().add(selection);
});
wv.setOnMouseDragged(event -> {
selection.setWidth(Math.abs(event.getX() - anchor.getX()));
selection.setHeight(Math.abs(event.getY() - anchor.getY()));
selection.setX(Math.min(anchor.getX(), event.getX()));
selection.setY(Math.min(anchor.getY(), event.getY()));
});
wv.setOnMouseReleased(event -> {
// Do what you want with selection's properties here
System.out.printf("X: %.2f, Y: %.2f, Width: %.2f, Height: %.2f%n",
selection.getX(), selection.getY(), selection.getWidth(), selection.getHeight());
// root.getChildren().remove(selection);
// selection.setWidth(0);
// selection.setHeight(0);
});
wv.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent e1) -> {
if (e1.getCode() == KeyCode.SPACE ) {
selection.setFill(Color.WHITE); // transparent
}
});
wv.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(final KeyEvent keyEvent) {
if (keyEvent.getCode() == KeyCode.F5) {
System.out.println("F5 pressed");
//Stop letting it do anything else
// WritableImage croppedImage = selection.snapshot(null, null);
WritableImage image = selection.snapshot(new SnapshotParameters(), null);
// TODO: probably use a file chooser here
File file = new File("C:/temp/snapshot.jpg");
try {
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "jpg", file);
} catch (IOException e) {
// TODO: handle exception here
}
}
System.out.println("snapshot saved: " );
}
});
root.getChildren().add(wv);
Scene scene = new Scene(root, 300, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Primary Stage");
primaryStage.show();
}
}
this code i tried and its running fine but the image that is saved is saved with white color its not saved the orginal selected image so how can i do it please help i need to save the selected image and i had done white but not with white color but the selected image so please tell me how to do it and what im doing wrong.
It seems there are two things going on here.
Firstly, instead of taking a snapshot of the Rectangle you are using to mark the selection, you probably want to take a snapshot of the root and use the selection rectangle to specify what part of the snapshot to take. So, instead of writing
WritableImage image = selection.snapshot(new SnapshotParameters(), null);
try writing
SnapshotParameters params = new SnapshotParameters();
params.setViewport(
new Rectangle2D(
selection.getX(),
selection.getY(),
selection.getWidth(),
selection.getHeight()));
root.getChildren().remove(selection);
WritableImage image = root.snapshot(params, null);
I've also removed the rectangle from the root so that it doesn't appear in the output image.
The second thing that happens is that the image comes out pink. This appears to be an existing issue which is supposed to have been fixed in Java 8 but I can still reproduce it in Java 8 update 152. Adapting the code in the question I linked to, you would have to replace the line
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "jpg", file);
with
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
BufferedImage imageRGB = new BufferedImage(
bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.OPAQUE);
Graphics2D graphics = imageRGB.createGraphics();
graphics.drawImage(bufferedImage, 0, 0, null);
ImageIO.write(imageRGB, "jpg", file);
I have this png file and converted it to jpg image.
As you can see, some pixels around the circles are not as smooth as the circles in the png image.
I have this code to convert png to jpeg. How do I update the code so that I can remove the noise around the circles.
BufferedImage bufferedJpgImage = new BufferedImage(inputPngImage.getWidth(null),
inputPngImage.getHeight(null),
BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(inputPngImage, 0, 0, bufferedJpgImage.getWidth(), bufferedJpgImage.getHeight(), Color.WHITE, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(bufferedJpgImage, "jpg", out);
Jpeg uses a lossy compression algorithm, this means that the physical image data is modified in order to reduce the image size.
You can set the compression level through ImageIO, it's a little messy, but it should provide you the control you need.
So based the answer from Setting jpg compression level with ImageIO in Java, you could do something like:
The left is the original BufferedImage and the right is the resulting JPEG
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
try {
BufferedImage original = ImageIO.read(...);
BufferedImage bufferedJpgImage = new BufferedImage(original.getWidth(null),
original.getHeight(null),
BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedJpgImage.createGraphics();
g.drawImage(original, 0, 0, original.getWidth(), original.getHeight(), Color.WHITE, null);
g.dispose();
File jpg = new File("tmp.jpg");
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
write(bufferedJpgImage, baos);
try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())) {
BufferedImage image = ImageIO.read(bais);
JPanel panel = new JPanel();
panel.add(new JLabel(new ImageIcon(original)));
panel.add(new JLabel(new ImageIcon(image)));
JOptionPane.showMessageDialog(null, panel);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void write(BufferedImage capture, OutputStream to) throws IOException {
// use IIORegistry to get the available services
IIORegistry registry = IIORegistry.getDefaultInstance();
// return an iterator for the available ImageWriterSpi for jpeg images
Iterator<ImageWriterSpi> services = registry.getServiceProviders(ImageWriterSpi.class,
new ServiceRegistry.Filter() {
#Override
public boolean filter(Object provider) {
if (!(provider instanceof ImageWriterSpi)) {
return false;
}
ImageWriterSpi writerSPI = (ImageWriterSpi) provider;
String[] formatNames = writerSPI.getFormatNames();
for (int i = 0; i < formatNames.length; i++) {
if (formatNames[i].equalsIgnoreCase("JPEG")) {
return true;
}
}
return false;
}
},
true);
//...assuming that servies.hasNext() == true, I get the first available service.
ImageWriterSpi writerSpi = services.next();
ImageWriter writer = writerSpi.createWriterInstance();
// specifies where the jpg image has to be written
writer.setOutput(ImageIO.createImageOutputStream(to));
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(null);
jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpegParams.setCompressionQuality(1f);
// writes the file with given compression level
// from your JPEGImageWriteParam instance
writer.write(null, new IIOImage(capture, null, null), jpegParams);
}
}
You need to make sure you're managing your resources properly, closing or disposing of them when you have finished with them
What you are seeing is a natural consequence of JPEG compression in non-photographic images. JPEG relies on people not being able to notice small graduations in photographic images. When there is an abrupt change from one color to another, you get the artifacts you see.
You can reduce this effect by quantization table selection (quality settings in most encoders) and not sampling the Cb and Cr components at a lower rate than Y.
However, for an image like what you are showing, you are better off sticking with PNG.
Is it somehow possible to render a GUI to a BufferedImage or another kind of memory image without displaying it on a screen ?
I know this will loose all kinds of hardware acceleration, but for a simple GUI that is refreshed only once or twice a second this should not be an issue.
Already tried to get JavaFX to output an image, but i can't find a way to leave out rendering on a screen first. Does anyone know a way to do this with JavaFX or Swing ?
It is no problem to draw a simple GUI myself using simple image manipulations, but then i would have to do it all by hand and using Swing or FX would make it much easier.
Edit:
To make it a bit more clear, i don't have an active display, but i can save an image which then gets displayed through other means. To be exact its a raspberry pi, but without a primary display device with a connected tft display using the GPIO port. So i can't render the UI directly to a display device, but need to create an image that i can save at a specific location. All methods i have tried so far need a primary display device.
Yes, it is possible to render a GUI to an image offscreen.
Here is a sample using JavaFX, with example image output as below:
The example works by rendering the chart to an scene which is not added to any window and no window (Stage in JavaFX terminology) is ever shown. The snapshot method is used to take a snapshot of the node and then ImageIO utilities are used to save the snapshot to disk.
Rendering of the offscreen scene will be hardware accelerated if the underlying hardware/software platform supports it.
import javafx.application.*;
import javafx.collections.*;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.*;
import javafx.scene.chart.PieChart;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.*;
public class OffscreenImageRecorder extends Application {
private static final Logger logger = Logger.getLogger(OffscreenImageRecorder.class.getName());
private static final String IMAGE_TYPE = "png";
private static final String IMAGE_FILENAME = "image." + IMAGE_TYPE;
private static final String WORKING_DIR = System.getProperty("user.dir");
private static final String IMAGE_PATH = new File(WORKING_DIR, IMAGE_FILENAME).getPath();
private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
private final Random random = new Random();
private final int CHART_SIZE = 400;
#Override
public void start(Stage stage) throws IOException {
Parent chart = createChart();
Image image = snapshot(chart);
exportPng(SwingFXUtils.fromFXImage(image, null), IMAGE_PATH);
Platform.exit();
}
private Parent createChart() {
// create a chart.
final PieChart chart = new PieChart();
ObservableList<PieChart.Data> pieChartData =
FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", random.nextInt(30)),
new PieChart.Data("Oranges", random.nextInt(30)),
new PieChart.Data("Plums", random.nextInt(30)),
new PieChart.Data("Pears", random.nextInt(30)),
new PieChart.Data("Apples", random.nextInt(30))
);
chart.setData(pieChartData);
chart.setTitle("Imported Fruits - " + dateFormat.format(new Date()));
// It is important for snapshots that the chart is not animated
// otherwise we could get a snapshot of the chart before the
// data display has been animated in.
chart.setAnimated(false);
chart.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
chart.setPrefSize(CHART_SIZE, CHART_SIZE);
chart.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
return chart;
}
private Image snapshot(final Parent sourceNode) {
// Note: if the source node is not in a scene, css styles will not
// be applied during a snapshot which may result in incorrect rendering.
final Scene snapshotScene = new Scene(sourceNode);
return sourceNode.snapshot(
new SnapshotParameters(),
null
);
}
private void exportPng(BufferedImage image, String filename) {
try {
ImageIO.write(image, IMAGE_TYPE, new File(filename));
logger.log(Level.INFO, "Wrote image to: " + filename);
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
public static void main(String[] args) {
launch(args);
}
}
It's a bit of a hack, but you could create a frame and position it on a invisible location (using Swing in this example):
frame = new JFrame("Invisible frame");
frame.setBounds(-1000, 100, 640, 480);
I have an image and I want to display it in the applet, The problem is the image wont display. Is there something wrong with my code?
Thanks...
Here's my code :
import java.applet.Applet;
import java.awt.*;
public class LastAirBender extends Applet
{
Image aang;
public void init()
{
aang = getImage(getDocumentBase(), getParameter("images.jpg"));
}
public void paint(Graphics g)
{
g.drawImage(aang, 100, 100, this);
}
}
aang = getImage(getDocumentBase(), getParameter("images.jpg"));
I suspect you are doing something wrong, and that should be just plain:
aang = getImage(getDocumentBase(), "images.jpg");
What is the content of HTML/applet element? What is the name of the image? Is the image in the same directory as the HTML?
Update 1
The 2nd (changed) line of code will try to load the images.jpg file in the same directory as the HTML.
Of course, you might need to add a MediaTracker to track the loading of the image, since the Applet.getImage() method returns immediately (now), but loads asynchronously (later).
Update 2
Try this exact experiment:
Save this source as ${path.to.current.code.and.image}/FirstAirBender.java .
/*
<applet class='FirstAirBender' width=400 height=400>
</applet>
*/
import javax.swing.*;
import java.awt.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class FirstAirBender extends JApplet {
Image aang;
public void init() {
try {
URL pic = new URL(getDocumentBase(), "images.jpg");
aang = ImageIO.read(pic);
} catch(Exception e) {
// tell us if anything goes wrong!
e.printStackTrace();
}
}
public void paint(Graphics g) {
super.paint(g);
if (aang!=null) {
g.drawImage(aang, 100, 100, this);
}
}
}
Then go to the prompt and compile the code then call applet viewer using the source name as argument.
C:\Path>javac FirstAirBender.java
C:\Path>appletviewer FirstAirBender.java
C:\Path>
You should see your image in the applet, painted at 100x100 from the top-left.
1) we living .. in 21century, then please JApplet instead of Applet
import java.awt.*;
import javax.swing.JApplet;
public class LastAirBender extends JApplet {
private static final long serialVersionUID = 1L;
private Image aang;
#Override
public void init() {
aang = getImage(getDocumentBase(), getParameter("images.jpg"));
}
#Override
public void paint(Graphics g) {
g.drawImage(aang, 100, 100, this);
}
}
2) for Icon/ImageIcon would be better to look for JLabel
3) please what's getImage(getDocumentBase(), getParameter("images.jpg"));
there I'll be awaiting something like as
URL imageURL = this.getClass().getResource("images.jpg");
Image image = Toolkit.getDefaultToolkit().createImage(imageURL);
Image scaled = image.getScaledInstance(100, 150, Image.SCALE_SMOOTH);
JLabel label = new JLabel(new ImageIcon(scaled));
Well , above answers are correct. This is the code I used to display image. Hope it helps:
/*
<applet code = "DisplayImage.class" width = 500 height = 300>
</applet>
*/
import java.applet.Applet;
import java.awt.*;
public class DisplayImage extends Applet
{
Image img1;
public void init(){
img1 = getImage(getCodeBase(),"Nature.jpg" );
}
public void paint(Graphics g){
g.drawImage(img1, 0,0,500,300,this);
}
}
In above code, we create an image class object and get image from location specified by codebase. Then plot the image using drawImage method. Those who are interested in knowing value of getCodeBase() and getDocumentBase() methods can add following code in paint method. They are actually location of src folder in your project folder:-
String msg;
URL url=getDocumentBase();
msg="Document Base "+url.toString();
g.drawString(msg,10,20);
url=getCodeBase();
msg="Code Base "+url.toString();
g.drawString(msg,10,40);
One more point to note:- Make sure images and classes don't have same name in src folder. This was preventing my image to be displayed.
Is it possible to skew or distort an Image object in Java? I 'pull' one side of an image out, making it seem closer to me. (LIke 3D).
Any suggestions?
Yes. Lots of ways but I would start with the Advanced Imaging API. It provides a ton of advanced imaging functionality.
But just to do the type of transform that you're talking about you might just need an Affine Transform. Sample results here for the previous link.
You can also do this with JavaFX.
The following example uses PerspectiveTransform and a bit of rotation on the BufferedImage.
It turns this image
into this
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
/**
* Distorts images using transformations.
* <p>
* Created by Matthias Braun on 2018-09-05.
*/
public class Distortion {
public static void main(String... args) throws IOException {
URL imgUrl = new URL("https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a");
BufferedImage img = ImageIO.read(imgUrl);
BufferedImage distorted = distortImg(img);
File newImgFile = new File(System.getenv("HOME") + "/distorted.png");
System.out.println("Saving to: " + newImgFile);
ImageIO.write(distorted, "png", newImgFile);
// Since we started a JavaFX thread in distortImg we have to shut it down. Otherwise the JVM won't exit
Platform.exit();
}
/**
* Applies perspective transformations to a copy of this {#code image} and rotates it.
* <p>
* Since this method starts a JavaFX thread, it's important to call {#link Platform#exit()} at the end of
* your application. Otherwise the thread will prevent the JVM from shutting down.
*
* #param image the image we want to distort
* #return the distorted image
*/
private static BufferedImage distortImg(BufferedImage image) {
// Necessary to initialize the JavaFX platform and to avoid "IllegalStateException: Toolkit not initialized"
new JFXPanel();
// This array allows us to get the distorted image out of the runLater closure below
final BufferedImage[] imageContainer = new BufferedImage[1];
// We use this latch to await the end of the JavaFX thread. Otherwise this method would finish before
// the thread creates the distorted image
final CountDownLatch latch = new CountDownLatch(1);
// To avoid "IllegalStateException: Not on FX application thread" we start a JavaFX thread
Platform.runLater(() -> {
int width = image.getWidth();
int height = image.getHeight();
Canvas canvas = new Canvas(width, height);
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
ImageView imageView = new ImageView(SwingFXUtils.toFXImage(image, null));
PerspectiveTransform trans = new PerspectiveTransform();
trans.setUlx(0);
trans.setUly(height / 4);
trans.setUrx(width);
trans.setUry(0);
trans.setLrx(width);
trans.setLry(height);
trans.setLlx(0);
trans.setLly(height - height / 2);
imageView.setEffect(trans);
imageView.setRotate(2);
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
Image newImage = imageView.snapshot(params, null);
graphicsContext.drawImage(newImage, 0, 0);
imageContainer[0] = SwingFXUtils.fromFXImage(newImage, image);
// Work is done, we decrement the latch which we used for awaiting the end of this thread
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return imageContainer[0];
}
}