Display JavaFX Image without blur/dithering when zooming in - javafx

I'm trying to create a image editor tool, where I have X- and Y-axis. Now I want to support the user by seeing each pixel when zooming in so that they can work more exactly. In comparison you should take a look at paint.net where you can see the pixels when zooming in.
I'll give you a code snippet and perhaps you might have a hint for me where I should dig into (API or code examples). I've found nothing like it in the JAVAFX API documentation. I want to use an ImageView instance but not a Canvas.
ScrollPane scrollPane = new ScrollPane ();
BorderPane borderPane = new BorderPane();
Group group = new Group();
DoubleProperty zoomProperty = new SimpleDoubleProperty(1);
Scale scaleTransformation = new Scale();
scaleTransformation.xProperty().bind(zoomProperty);
scaleTransformation.yProperty().bind(zoomProperty);
ImageView viewer = new ImageView(getBackgroundImage());
viewer.setFitWidth(600);
viewer.setPreserveRatio(true);
viewer.setDisable(true);
viewer.setSmooth(false);
group.getChildren().add(viewer);
this.addEventFilter(ScrollEvent.SCROLL, new EventHandler<ScrollEvent>(){
#Override
public void handle(ScrollEvent se) {
if (se.isControlDown() && se.getDeltaY() > 0) {
double value = zoomProperty.get() * 1.1;
zoomProperty.set(value);
se.consume();
} else if (se.isControlDown() && se.getDeltaY() < 0) {
double value = zoomProperty.get() / 1.1;
zoomProperty.set(value);
se.consume();
}
}
});
borderPane.setCenter(group);
scrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS);
scrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);
scrollPane setContent(new Group(borderPane));
Thanks for you help.
EDIT
The related question and it's answers do not solve my problem (JavaFX ImageView without any smoothing). The solution 1-4 do not work. Only the hack does. I guess I could be related the node structure I use. I've adjusted my code.

I have found one solution (referring to the possible duplicate) and using a variant of method 1.
In my image constructor I set the requested size to something large - so for an image that I know is about 1000 pixels wide I request an image 10000 pixels wide:
Image image = new Image(file.toURI().toString(), 10000.0, 10000.0, true, false);
I am not sure if this is giving a true view of the pixels, or if it is smoothing at a very low level...... but it works for my needs.

Related

Get visible elements from AnchorPane

I'm developing a JavaFX application where the user is able to zoom and drag the elements (all contained in a AnchorPane). Some of those elements are simple lines and I need to have something like a ruler that has different parent to stick on the screen on the same position even if the user zooms or drags the mentioned AnchorPane. I got almost everything working but I have one problem, I need to know which lines from the AnchorPane are visible to the user (as if the user zooms and drags the AnchorPane, some of the lines are not visible anymore). Here's what I tried (not working...)
private List<Double> getVisibleVerticalLinesXCoordonate() {
List<Double> xCoordonatesOfVisibleVerticalLines = new ArrayList<>();
List<Node> visibleNodes = new ArrayList<>();
Bounds bounds = rulerParent.getBoundsInLocal();
Bounds paneBounds = rulerParent.localToScene(bounds);
for (Node n : gridVerticalLines) {
Bounds nodeBounds = n.getBoundsInParent();
if (paneBounds.intersects(nodeBounds)) {
visibleNodes.add(n);
}
}
for (Node node : visibleNodes) {
Bounds newBounds = getRelative(node);
xCoordonatesOfVisibleVerticalLines.add(newBounds.getMinX());
}
System.out.println(Arrays.asList(xCoordonatesOfVisibleVerticalLines));
return xCoordonatesOfVisibleVerticalLines;
}
private Bounds getRelative(Node node) {
return rulerParent.sceneToLocal(node.localToScene(node.getBoundsInLocal()));
}
So, the rulerParent is what is fixed on the screen (is not zooming or dragging at all). After I have the visible lines from the AnchorPane I get the x coordinates of the lines relative to rulerParent - so I can align the ruler lines with the lines in the AnchorPane.
The problem is that this is not returning the actual visible lines...
I don't need to be able to see the whole line to consider it visible, that's why I'm using intersect...if any part of a line is visible, I need it.
It's about how you handle the zoom and dragging actions try to use ViewPort instead of scaling as with ViewPort you can know the translation X and Y coordinates of the ViewPort which is the visible X and Y coordinates of the AnchorPane

JavaFX get minimum width/height/size of a Pane before it starts clipping

FFor the sake of simplicity it is better to show you the problem and then describe it
I plan to set the minimum width of the window to the minimum size possible before the pane (groupPane in this example) starts being clipped
how can i calculate that width beforehand.
the fxml section :
the current calculation method:
public void setupMinStageSizeInStandardMode(Stage primaryStage, Scene scene)
{
// the thickness of the border of the window
ObjectBinding<Insets> insets = Bindings.createObjectBinding(() ->
new Insets(scene.getY(),
primaryStage.getWidth()-scene.getWidth() - scene.getX(),
primaryStage.getHeight()-scene.getHeight() - scene.getY(),
scene.getX()),
scene.xProperty(),
scene.yProperty(),
scene.widthProperty(),
scene.heightProperty(),
primaryStage.widthProperty(),
primaryStage.heightProperty()
);
//i am trying to get the grid to the smallest size here
controlsGrid.setMaxWidth(0);
controlsGrid.setPrefWidth(0);
// then i am trying to calculate the wanted size
double minStageWidth = controlsGrid.getWidth() +insets.get().getLeft() + insets.get().getRight();
double minStageHeight = controlsGrid.getHeight() +insets.get().getTop() + insets.get().getBottom();
System.out.println(minStageWidth);
System.out.println(minStageHeight);
//then return the grid to it's intended size
controlsGrid.setMaxWidth(Region.USE_PREF_SIZE);
controlsGrid.setPrefWidth(Region.USE_PREF_SIZE);
primaryStage.setMinHeight(minStageHeight);
primaryStage.setMinWidth(minStageWidth);
}
Please help. i am losing sleep over this...

JavaFX - Slim images to prevent overloading the heapspace?

Due to programming an Image Viewer with JavaFX I implemented an image library with following structure:
HBox - ScrollPane - VBox
This is the basic structure, the library objects are constructed as following:
StackPane - ImageView - Label
Because there's no setAlignment method for ScrollPane, I put the whole thing in a HBox, even though I don't need the other features of it.
These methods are responsible for the generation and initialization of the library:
private HBox createScrollableLibrary()
{
libraryContent = new VBox();
libraryContent.setPadding(new Insets(10));
libraryContent.setAlignment(Pos.CENTER);
libraryContent.setStyle("-fx-background-color: transparent");
libraryContent.setSpacing(10);
libraryScroll = new ScrollPane();
libraryScroll.setHbarPolicy(ScrollBarPolicy.NEVER);
libraryScroll.setStyle("-fx-background: rgb(0, 0, 0, 0.3);"
+ "-fx-background-color: rgb(0, 0, 0, 0.3);");
libraryScroll.setMinWidth(135);
libraryScroll.setMaxWidth(135);
libraryScroll.setContent(libraryContent);
HBox root = new HBox();
root.setPickOnBounds(false);
root.getChildren().add(libraryScroll);
return root;
}
And:
public void initLibrary()
{
Runnable work = () ->
{
ObservableList<StackPane> previewList = FXCollections.observableArrayList();
ImageView preview;
Label label;
StackPane previewAndLabel;
for(int i = 0; i < picturePaths.size(); i++)
{
String path = picturePaths.get(i).toURI().toString();
preview = new ImageView(new Image(path));
preview.setPreserveRatio(true);
preview.setFitWidth(100);
//label = new Label(new File(path).getName());
label = new Label("IMAGE");
StackPane.setAlignment(label, Pos.CENTER_LEFT);
previewAndLabel = new StackPane();
previewAndLabel.getChildren().addAll(preview, label);
previewList.add(previewAndLabel);
}
Platform.runLater(() -> GUI.getLibraryContent().getChildren().addAll(previewList));
};
loading = new Thread(work);
if(loading.isAlive())
loading.interrupt();
loading.start();
}
Info: picturePaths is an ObservableList which contains the paths of all selected images which in turn are selected with a FileChooser.
Now my question(s):
I know it's very inefficient to use large sized pictures as preview images.
Even though I increased my heap space to 1GB it slows down and throws a java.lang.OutOfMemoryError exception when I'm adding more pictures, I'm guessing the limit is about 20-25 of large sized pictures.
How can I "slim" a normal picture with, for example a resolution of 1920x1080 to 1024x720 ? Or is there another opportunity to make my library use small and "fast" images? And can I buffer my already "calculated" images to make my Image Viewer faster?
Appreciate any tips and help. Please critisize my code if needed!
One can try setting the size constraints on the preview pictures during the Image constructor and not afterwards.

Forcing layout change when Stage is resized in JavaFX

I ran into a possible bug in JavaFX, but having short time, I am looking for a workaround.
I have a bunch of windows containing a chart and a toolbar:
public void createGui() {
root = new VBox();
ToolBar toolbar = new ToolBar();
Button btnConfig = new Button(bundle.getString("chartWindow.menu.configure"));
btnConfig.setOnAction(e -> doChartConfig());
toolbar.getItems().addAll(btnConfig);
chartPane = new VBox();
root.getChildren().setAll(toolbar, chartPane);
VBox.setVgrow(chartPane, Priority.ALWAYS);
scene = new Scene(root);
setScene(scene);
updateChart(); // Creates a chart
}
I have to resize them from code. (Tiling the open charts.) The simplified tiling code is:
// bounds is the user available area of the monitors(s)
double windowWidth = bounds.getWidth() / tileRule.columns;
double windowHeight = bounds.getHeight() / tileRule.rows;
int r = 0;
int c = 0;
for (Window w : sel) {
w.setWidth(windowWidth);
w.setHeight(windowHeight);
w.setX(bounds.getMinX() + c * windowWidth);
w.setY(bounds.getMinY() + r * windowHeight);
c++;
if (c >= tileRule.getColumns()) {
c = 0;
r++;
if (r >= tileRule.rows) {
break;
}
}
}
When I do this, the windows are perfectly arranged:
However, as it is visible, some of the windows contents aren't resized with the window (Now, accidentally, they are the last 3, but it is not always the case. Sometimes there are more, and they are not always the last ones.)
It is clearly visible, that the scene is the one that isn't resized with the window.
As soon as I resize the window manually, the controls are layed out well.
I trled a bunch of things to hack this:
Calling the requestLayout method manually
Removing the root element from the scene and adding again
Removing the scene from the stage and adding again
Adding 100 ms delay between the resize operations
binding the root elements width to the window width (minus border size)
None of the above helped. (Yes, not even adding the scene again! It caused the most spectacular result, because the contents are resized to the window size, but with streaching its content.)
Does anyone have any other idea how to hack this bug?
I'm using Java 8u74.

How to increse performance of raster scrolling on Mac?

I have a game with a big raster map
Now we are using jpeg (4900x4200)
And durring the game we need to scroll through this map.
We use the following:
Class Map extends mx.containers.Canvas
and mx.controls.Image on it
In constructor we have:
public function Map() {
super();
image.source = ResourceManager.interactiveManager.map;//big image
addChild(image);
......
}
for scrolling we are use:
if(parentAsCanvas==null){
parentAsCanvas = (parent as Canvas);
}
parentAsCanvas.verticalScrollPosition = newX;
parentAsCanvas.horizontalScrollPosition = newY;
In windows, we have very good performance.
In Linux and Mac in flashplayer we have a good performance too.
But in browsers performance is quite slow!
What can we do to resolve it?
It's slow because you're rendering a large image all the time.
Here are a few things that cross my mind:
Try using the scrollRect property in a Bimtap object holding your image BitmapData to display just the visible area then use the scrollRect x and y to move to a new region
Try using a BitmapData the size of the viewable area and use copyPixels() to get the right area to display, again using a rectangle
Try using BitmapData.scroll()
Here are a few snippets:
scrollRect:
//assuming map is BitmapData containing your large image
//100x100 is a test scroll area
var scroll:Rectangle = new Rectangle(0,0,100,100);
var bitmap:Bitmap = new Bitmap(map);
bitmap.scrollRect = scroll;
addChild(bitmap);
this.addEventListener(Event.ENTER_FRAME, update);
function update(event:Event):void{
scroll.x = mouseX;
scroll.y = mouseY;
bitmap.scrollRect = scroll;
}
copyPixels:
var scroll:Rectangle = new Rectangle(0,0,100,100);
var scrollPoint:Point = new Point();
var map:BitmapData = new Map(0,0);
var bitmap:Bitmap = new Bitmap(new BitmapData(100,100,false));
bitmap.bitmapData.copyPixels(map,scroll,scrollPoint);
addChild(bitmap);
this.addEventListener(Event.ENTER_FRAME, update);
function update(event:Event):void{
scroll.x = mouseX;
scroll.y = mouseY;
bitmap.bitmapData.fillRect(scroll,0xFFFFFF);
bitmap.bitmapData.copyPixels(map,scroll,scrollPoint);
}
Not perfect, but it should give you an idea
HTH,
George
I've read the following acrticle http://www.insideria.com/2008/04/flex-ria-performance-considera.html
I and i found the resolve of my problem.
If i open my SWF in browser as "http://host/myswf.swf" I have huge performance lose in browser as the reason work LaoyoutManager, that recalculates positions and sizes of all canvases in the application. And it process eats more than 60% of performance capacity.(Yep, we have a lot of Canvases in our application).
And when i putted my application in contant-size html block in html page, all problems were go away! I've got 80% performance increase!

Resources