Shrinking JavaFX Graphics - java

I'm looking for a way of rendering a scene hidden from view and then creating a second shrunken copy for initial display. Plainly I could render the contents a second time to create this copy only smaller. The original code written in another language though produced a better result using a smooth (spatially filtered) shrinking function to create the small copy. Is there such a bitmap copy/shrink/smooth function in JavaFX and is it any good?

You can create a snapshot of a scene as a WritableImage as follows:
Scene scene = ... ;
Image image = scene.snapshot(null);
and then display a scaled version of it in an image view with:
ImageView sceneImage = new ImageView();
sceneImage.setFitWidth(desiredWidth);
sceneImage.setFitHeight(desiredHeight);
// force aspect ratio to be preserved if fitWidth and fitHeight would distort it:
sceneImage.setPreserveRatio(true);
// this provides a tiny bit of control over the scaling used:
sceneImage.setSmooth(true);
sceneImage.setImage(image);

Related

Optimal solution for Image scaling where i need to preserve aspect ratio and retain good quality

So here is the problem: I am loading an image which is of unknown resolution. I need to scale it to 500px width to fit within my UI, preserving aspect ratio. The image quality must be good as i need to use it later, specifically to calculate an angle between two objects within the image.
So far i have tried the following:
In the following examples, we have an existing UI element ImageView imageView;
Method one: using scale on load in JavaFX image constructor:
public void setImage(String path){
Image image = new Image(path, 550, 412, true, true);
imageView.setImage(image);
}
This method achieves the required scaling, but lacks the quality required for an accurate calculation of angle later on.
Method two: using ImageView scaling to scale the displayed image:
public void setImage(String path){
Image image = new Image(path);
imageView.setFitWidth(550);
imageView.setPreserveRatio(true);
imageView.setImage(image);
}
This achieves the required quality and scaling, but the image contained within the view is not actually scaled, which means the calculations done later down the line are using an image not representative of the one being displayed.
Is there a way i can alter either of these solutions to work better or is there a better approach i can take? thank you.
You will have to use,
imageView.setSmooth(true);
to achieve better quality image after scaling.
EDIT:
It is because, the two methods are essentially different. The first method loads a bad quality image in the first place (with lesser pixels). One thing you can try is, to use AWT Image class for loading the image, using ImageIO, and call scaledImage = image.getScaledInstance(width, height). Then use something like (pseudo code):
create bufferedImage of scaled size
bufferedImage.createGraphics()
.drawImage(scaledImage, 0, 0, null)
to get scaled AWT Image. Then use SwingFXUtils to convert to image for FX application. This whole thing can probably be done in 3 lines of code by chaining the methods. I was able to achieve better quality this way in one of my previous projects.

Alternative(s) of converting Canvas to WritableImage

I'm developing an application which needs a lot of comparing image data in JavaFX. For that comparing, I need to to get rgb data of pixels in the images. Let's say that the picture I need to compare is Picture1 and Picture2. Picture1 is an input picture selected by the user. Picture2 is a drawing that I make programmatically.
AFAIK, the only way to get rgb data per pixel in JavaFX is with PixelReader of Image. It's fine for the Picture1 since it's an input. But I need to do a drawing on Picture2 and Picture2 is a Canvas (I draw polygons on Picture2 and because drawing pixel by pixel with PixelWriter is a bit complicated IMO) and doesn't have a PixelReader or something with similar functionality. So my solution is as follows:
Read input image and make Picture1
Make a Canvas and do the drawing
Attach the Canvas to a Scene
Make snapshot of the Scene and make WritableImage (Picture2)
Compare Picture1 and Picture2
So I have a method with something like:
public static WritableImage makeDrawableImage(Canvas in) {
AnchorPane root = new AnchorPane(in);
WritableImage out = new WritableImage((int)in.getWidth(), (int)in.getHeight());
Scene sc = new Scene(root, in.getWidth(), in.getHeight());
out = in.snapshot(null, null);
return out;
}
However it takes a really long time for the snapshot to finish. The value of out never gets assigned. I don't know what I did wrong.
Is there any alternative to convert Canvas to Image or WritableImage? Or maybe there is a library to do drawing of polygons easier with PixelWriter?
Please be soft on me. This is my first question on Stack Overflow and I'm not a real programmer nor an IT student. I code as a hobby.
So, thanks in advance. Cheers.
You can create a snapshot of a canvas with this method:
public static WritableImage pixelScaleAwareCanvasSnapshot(Canvas canvas, double pixelScale) {
WritableImage writableImage = new WritableImage((int)Math.rint(pixelScale*canvas.getWidth()), (int)Math.rint(pixelScale*canvas.getHeight()));
SnapshotParameters spa = new SnapshotParameters();
spa.setTransform(Transform.scale(pixelScale, pixelScale));
return canvas.snapshot(spa, writableImage);
}
The pixel scale normally is 1.0 unless you have a HiDPI screen.

Display the modified image of an ImageView object

I've been using an ImageView object to try and modify an Image and then draw it. However I could only make the two following outcomes happen:
1) After ImageView has been modified, I tried to grab the image out of it for display with getImage() but that only displayed the original Image I had in the first place.
example code:
sheet = new Image("file:sprite_sheet.png");
ImageView iv = new ImageView();
iv.setImage(sheet);
iv.setViewport(new Rectangle2D(0, 0, 32, 32));
g.drawImage(iv.getImage(), 100, 100);//'g' is GraphicsContext2D
2) I managed to get the modified image to show by adding it to the Group obj like so:
root.getChildren().add(iv);
But then it only displayed the image in the top left corner of the screen and I couldn't move it or do anything with it.
I prefer (if possible) to approach this issue with a method more like the first outcome has presented. And just saying this in advance, I'm aware of that that there's another post similar to this atm, but I couldn't get anything useful out of it.
ImageView, as the name implies, is just a view of the data (image in this case). All modifications to the view only affect the view and not the data. There are two general methods for what you want to achieve.
Use scene graph and ImageView object. Perform all visual operations on the view. This is the preferred way as the data stays the same, meaning you can easily create a new ImageView with the original image and modify it in a different way should you wish to. If the parent is Pane, then you can easily move the object by calling setTranslateX/Y.
If you absolutely need to use Canvas, then you will need to modify the data itself or create a copy of the data and modify it instead. You can do this by using a combination of the following: PixelWriter and PixelReader
Example for 1.:
ImageView view = new ImageView(image);
// create root with dimension 800x600
Pane root = new Pane();
root.setPrefSize(800, 600);
// attach view to root so it can be displayed
root.getChildren().add(view);
// translate view to 100, 100
// on the screen this means the image will move 100 in X and 100 in Y
view.setTranslateX(100);
view.setTranslateY(100);

JavaFX- Don't scale image for stage icon?

I am setting an image to my Stage so that becomes the logo for the JavaFX application.
stage.getIcons().add(new Image("myLogo.png"));
However, the image gets scaled and stretched horizontally since it is not at a ratio it likes.
Is there a way can manipulate the image so it is centered in its placement, or add margins so it matches the expected ratio?
I don't want to create a whole new image if I can help it.

SWT: draw "icons" in table or tree cells

We are having a Swing application which we plan to port to SWT/JFace. The Swing application draws a lot of icons (javax.swing.Icon implementations) in trees and tables (icon left to the text). If I understood it correctly, SWT only can draw images (aka graphic files). What would be the simplest solution to paint the icon or entire table/tree cell? Thanks in advance.
Images can be loaded from graphic files, or they can be drawn in-memory.
Image image = new Image(Display.getCurrent(), width, height);
GC gc = new GC(image);
// draw icon using GC
gc.dispose();
As far as displaying them in tables/trees, the most direct approach is to set the image on the table/tree item:
TableItem item = ...
item.setImage(theImage);
There is one significant drawback to this approach however: on Windows, the first time you set an image on a table item, that image's height becomes the standard height for all items in the table. So if you set a larger image, it will be scaled down to the first image's size. If you set a smaller image, it will be scaled up.
If all your images are the same size, this will not be a problem.
However if you can not predict the size of the images in advance, I recommend using the custom draw API to render your table/tree items. This approach is definitely more intensive but gives you fine-grained control over the result.

Categories

Resources