How to convert an image into a shape in Java? - java

I'm currently working on a stock market program that teaches students how to interact with the stock market. An issue that I'm working on right now revolves around the issue of efficiency and memory. I make all of my 2D icons (such as a settings icon, portfolio icons, etc) in adobe illustrator, I export those files as png files, and throw them into my program. I currently use JavaFX, and one of the features of JavaFX is what's called imageView, which is a method that handles the opening and viewing of images.
So let's say that the user would like to press on the Settings icon in the game, I would like to change the settings icon to a darker or lighter color when the user hovers over that icon in order to provide the user with a better user experience (UX) at the moment, I use two different images and remove one from the frame, and replace it with another, which is highly inefficient.
I know that JavaFX has a Shape class that inherits many methods such as Fill, or setFill, but these methods can only affect those of the Shape class.
My question is, "How can I convert an image that is imported into the project, into something that I can use methods such as setFill and Fill on"

If you're only aiming for basic changes such as darkening or lightning your icons, you can look at the Effects part of javaFx, you can read about it here, or else you can import your images as SVG as suggested in the comments
If you're planning to do it using Effects, you can achieve a darken-on-hover effect using the ColorAdjust Effect by setting the brightness value to something negative (the brightness in ColorAdjust ranges between -1 and +1 where 0 is the default) as in the following example
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage s) {
ImageView image = new ImageView(new Image("img.png"));
ColorAdjust darker = new ColorAdjust();
darker.setBrightness(-.3);
image.setOnMouseEntered(e-> {
image.setEffect(darker);
});
image.setOnMouseExited(e-> {
image.setEffect(null);
});
s.setScene(new Scene(new StackPane(image)));
s.show();
}
public static void main(String[] args) {
launch(args);
}
}
Changing the color of an image might involve adjusting the hue using ColorAdjust by setting the hue value
ColorAdjust hueShift = new ColorAdjust();
hueShift.setHue(-.3);
image.setOnMouseEntered(e-> {
image.setEffect(hueShift);
});
image.setOnMouseExited(e-> {
image.setEffect(null);
});
You can combine effects by setting effects as input to other effects, so for example, if you want to darken a Node and blur it at the same time you can set the blur effect as input to the darkening colorAdjust
GaussianBlur blur = new GaussianBlur();
blur.setRadius(10);
ColorAdjust darker = new ColorAdjust();
darker.setBrightness(-.3);
darker.setInput(blur);
image.setOnMouseEntered(e-> {
image.setEffect(darker);
});
image.setOnMouseExited(e-> {
image.setEffect(null);
});

Related

JavaFX MediaView rendering a white video

TL;DR : Solution : Increase the JavaFX version to 15.0.1.
I am re-writing the question I asked yesterday because it was poorly formulated and poorly explained.
What I do : I use JavaFX to create a Media and a MediaView to render a .mp4 video in a scene.
What happens : The screen stays blank.
What should happen : The video should be rendered properly and visible by the user.
What I've tried :
Changing the file encoding (from H.264 to QuickTime (outputs as .mov)).
Result : QuickTime encoding isn't recognized by JavaFX.
Changing the FPS value from 30 to 60 and from 60 to 30.
Result : no difference.
Tweaking the file size by shortening the video.
Result : no difference.
Changing the video resolution scale from 16:9 to 16:10.
Result : no difference.
Changing the video resolution value from 2560x1440 to 1920x1080.
Result : the video is shown on the screen, but I need a 2560x1440 video to fill the screen. I will take care of different resolutions later on by myself.
Using different videos from my computer.
Result : resolutions less or equal to 1920x1080 are working fine. I tried video a different video with a 2560x1440 resolution and it does not work.
Using a 2560x1440 video referenced by an internet URL.
Result : same behavior as described above.
My code :
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import java.io.File;
public class Application extends javafx.application.Application {
Stage window;
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setWidth(2560);
window.setHeight(1440);
window.setX(0);
window.setY(0);
window.setResizable(false);
window.setFullScreen(true);
window.setFullScreenExitHint("");
Pane pane = new Pane();
// Example to show that adding a simple figure to the pane works fine.
javafx.scene.shape.Rectangle r = new javafx.scene.shape.Rectangle(0, 0, 150, 150);
Media media = new Media(new File(/*Insert file name you own for testing purposes*/).toURI().toString());
// The path I would use : "src\\main\\resources\\img\\Animated Titlescreen Background.mp4".
// This is obtained using other classes and methods that read the computer directories,
// so it works fine across different computers.
MediaPlayer mediaPlayer = new MediaPlayer(media);
mediaPlayer.setAutoPlay(true);
MediaView mediaView = new MediaView(mediaPlayer);
pane.getChildren().addAll(mediaView, r);
Scene scene = new Scene(pane, 2560, 1440);
window.setScene(scene);
window.show();
}
public static void main(String[] args) {
launch(args);
}
}
After reading more on this link, it says Windows 8 increase H.264 decoder resolution to 4096x2304, which is available on JavaFX version 15.0.1. I was using version 12.0.1 because of a critical issue on Linux with version 15.0.1.
Solution : Increase the JavaFX version to 15.0.1.

JavaFX ImageView display GIFanimation high GPU [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am creating JavaFX application and made a loading scene. In this scene i want to show some text like loading or initializing and add some loading spinner animated image as a visual notification that something is loading.
I generate a loading .gif animation created via https://loading.io/ and also downloaded it like frames of animation (multiple .png files).
I noticed that in the JavaFX Scene Builder 8.5.0 when I set the .gif in ImageView, my GPU in Windows task manager went up to 20%, on NVidia GeForce RTX 2070 graphic card and in my opinion this is a problem. I tested with a javafx desktop application, to rule out that is not a problem generated by the scene builder application and got similar results.
The next step that i tried is to create my custom animation using javafx.​animation.Timeline. This is the initializing function that i call from the controller initialize(URL location, ResourceBundle resources) function. This function doesn't use the .gif image, it uses the multiple .png images of the same .gif image as frames.
private void initializeAnimation() {
imgLoading.setCache(true);
imgLoading.setCacheHint(CacheHint.SPEED);
Image[] images = new Image[31];
for(int i = 0; i <= 30; i++){
images[i] = new Image(getClass().getResourceAsStream("/es/main/gui/javafx/images/loading/frame-" + i + ".png"));
}
Timeline timeLine = new Timeline();
Collection<KeyFrame> frames = timeLine.getKeyFrames();
Duration frameGap = Duration.millis(100);
Duration frameTime = Duration.ZERO;
for (Image img : images) {
frameTime = frameTime.add(frameGap);
frames.add(new KeyFrame(frameTime, e -> imgLoading.setImage(img)));
}
timeLine.setCycleCount(Timeline.INDEFINITE);
timeLine.play();
}
In the first version i didn't call .setCache() and .setCacheHint() functions, I added them later while testing different variations of the same code. Also i tried adding
-Dprism.forceGPU=true -Dsun.java2d.opengl=true -Dprism.order=es2,es1,sw,j2d
as VMOption or some variants that I have read on this forum on improving graphic related settings for java. At the end, after all the changes, results in my task manager didn't change drastically. In the current version i use up to 17% of my GPU on this scene.
When the scene ends, in the next scene with no .gif images or Timeline's my GPU drops to almost 0%.
Running configurations:
Processor: i9-9900KF
Graphic card: GeForce RTX 2070
Java version "1.8.0_251"
JavaFX version "8.0.251-b08"
Short summary question: How to display animated .gif images correctly in JavaFX without having drastic overhead on the GPU (or CPU when with integrated graphics).
(Edit) 14.09.2020 - Java naming conventions
First thing I didn't noticed that i was not using the same size on my ImageView, so the first thing i changed for testing is adjusting the ImageView the same size as my .gif image (same width and height on both ImageView and .gif image). With this change the GPU percentage lowered to about %5.
As suggested I also upgrading the Java and JavaFX versions:
Tried using the jdk 1.8.0_261 with JavaFX built in version 8.0.261-b12 and got similar results.
Tried using jdk 14.0.2 with JavaFX version 15+9 (latest openjfx-15) and still got similar results.
Short summary: Upgrading the Java and JavaFX version didn't change anything relating this issue. Using the same size helped, but I think I can improve even better with your help.
My guess is you'd probably see some performance gains if you used one or more RotateTransition rather than key frame animation.
Here's a simple example using multiple transitions in a ParallelTransition:
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 500, 200);
stage.setScene(scene);
Rectangle rect = new Rectangle (100, 40, 100, 100);
rect.setArcHeight(50);
rect.setArcWidth(50);
rect.setFill(Color.VIOLET);
RotateTransition rt = new RotateTransition(Duration.millis(3000));
rt.setByAngle(180);
rt.setAutoReverse(true);
FadeTransition ft = new FadeTransition(Duration.millis(3000));
ft.setFromValue(1.0);
ft.setToValue(0.3);
ft.setCycleCount(4);
ft.setAutoReverse(true);
ParallelTransition pt = new ParallelTransition(rect, ft, rt);
pt.play();
root.getChildren().add(rect);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

javaFx Image Height and Width got backwards?

I am making a game in javaFx. The way entities in my game is rendered is dependent on one of their fields called "angle", like this:
gc.save();
gc.transform(new Affine(new Rotate(angle, getCenterX(), getCenterY())));
gc.drawImage(image, x, y);
gc.restore();
"Gc" is the GraphicsContext. The getCenter methods will use the height and width of the images. However, the images are not rendered properly. I ran some tests and found that when I use the get Width method, it actually returns the height of the image. When I call the get Height method, it returns the width. My guess is that it's because the png images I use have been rotated 90 degrees in respect to their original state. However, when I go to preview and look at their size, it seems that everything is fine. I tried duplicating the images, but it doesn't really work. Any ideas how to fix this? I don't have a verifiable code example because I believe this question is more related to properties of png files and javaFx's Image methods than my program's logic.
Here is the verifiable code example:
import javafx.embed.swing.JFXPanel;
import javafx.scene.image.Image;
public class TestImage {
public static void main(String[] args) {
// TODO Auto-generated method stub
JFXPanel jfxPanel = new JFXPanel();
Image BLUE01 = new Image("/blue01.png");
System.out.println("height : "+ BLUE01.getHeight());
System.out.println("wdith : "+ BLUE01.getWidth());
}
}
And here is the image:
It seems that the problem comes from not my code but my use of IDE... It turns out that after I edit or add image in my resource folder, I need to click "refresh" in eclipse...

JavaFX - Music On/Off Toggle Button (Not Working)

Below is the code sample, with other features left out. The code below encompasses the media player only. It's used on a menu screen for a project I'm working on. My issue is getting the musicButton (which is a toggle button- On/Off) to work properly. Using the following code, when I interact with the music toggle button, the playing music stops. When I click it again to resume playing, it does not resume. It stops once and stops altogether.
You can see I've tried simply using the boolean values of the toggle button in two if statements... If it's off and pressed, pause the music. If its on and pressed, resume the music. The problem is, as stated earlier, pausing the music works but it cannot be resumed. I've tried some combinations with loops, but nothing worked either.
I think if statements are too simple for this. I've scoured the JavaDocs and various online articles but I cannot find anything definitive. I've read a little about listeners, but they seem overly-complex for an on/off switch.
My question:
How do I get the musicButton to pause/play the music, whenver the user clicks it?
Any help is appreciated, thanks.
-Bagger
/* A simple game, the mechanics not yet implemented.
This is simply working on the title screen. */
import java.io.File;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
public class MenuFX extends Application {
#Override
public void start (Stage primaryStage) {
// Make the window a set size...
primaryStage.setResizable(false);
// Create media player
// Rather than inputting the entire absolute URI, which would confine the program
// to the creator's device, we create a new file, grab the URI on whatever machine
// the program is running on and convert it to a string... portability.
Media menuMusic = new Media(new File("music/menu.mp3").toURI().toString());
MediaPlayer menuPlayer = new MediaPlayer(menuMusic);
// Want to see the absolute URI? Uncomment the next line
//System.out.println(new File("music/menu.mp3").toURI().toString());
// Adjust the cycles and volume then start playing menu music
// Lazy, but it will suffice
menuPlayer.setCycleCount(999999999);
menuPlayer.setVolume(0.1);
menuPlayer.setAutoPlay(true);
/*
Need assistance here
*/
// Create music toggle button
ToggleButton musicButton = new ToggleButton("Music On/Off");
if (musicButton.isSelected() == false) {
musicButton.setOnAction(e -> menuPlayer.pause());
}
if (musicButton.isSelected() == true) {
musicButton.setOnAction(e -> menuPlayer.play());
}
// Add all nodes to the vbox pane and center it all
// Must be in order from top to bottom
menuVBox.getChildren().add(musicButton);
menuVBox.setAlignment(Pos.CENTER);
// New scene, place pane in it
Scene scene = new Scene(menuVBox, 630, 730);
// Place scene in stage
primaryStage.setTitle("-tiles-");
primaryStage.setScene(scene);
primaryStage.show();
}
// Needed to run JavaFX w/o the use of the command line
public static void main(String[] args) {
launch(args);
}
}
I think you are over thinking it. You should have an EventListener on your ToggleButton to Pause and Play the music.
musicButton.setOnAction(event -> {
if (musicButton.isSelected()) {
menuPlayer.pause();
}else {
menuPlayer.play();
}
});
This should give you the desired effect.
The reason your code was not working is because the ToggleButton is not selected by default, so the only EventListener that gets associated with it is the menuPlayer.pause();. So when you click on it, it only ever pauses. I have moved your code into one EventListener, and used the approriate if-else.

JavaFX transparent window only receives mouse events over drawn pixels

I'd like a Stage that is the same size as the screen which is fully transparent and receives mouse events anywhere. In the example below I get mouse events only when the mouse is over the circle. I see this issue on both Windows XP and Windows 7 using Java 8u11
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage ignored) throws Exception {
Stage stage = new Stage(StageStyle.TRANSPARENT);
stage.setTitle("Transparent app test");
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
stage.setX(0);
stage.setY(0);
stage.setWidth(screenBounds.getWidth());
stage.setHeight(screenBounds.getHeight());
Circle circle = new Circle(100);
circle.setFill(Color.RED);
Rectangle rectangle = new Rectangle(screenBounds.getWidth(),
screenBounds.getHeight());
rectangle.setFill(Color.TRANSPARENT);
Scene scene = new Scene(new StackPane(circle, rectangle));
scene.setFill(null);
stage.setScene(scene);
scene.setOnMouseMoved((e) -> {
System.out.println("Mouse over rectangle " + e);
});
stage.show();
}
}
Interestingly if I set the alpha part of the fill color to its absolute minimum then I get mouse events. However I'd prefer not to use this workaround and actually get to the bottom of the issue. My conclusion is somewhere in JavaFX or a Windows library there is some hit-detection code that filters mouse events based on the pixel value of the mouse event.
rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events
rectangle.setFill(Color.rgb(0, 0, 0, 0)); // does not receive mouse events
Research
JavaFx Transparent window - yes please. Mouse transparent - no thanks describes a similar problem, however it does not address the issue of mouse events in completely transparent areas
Debugging - using a breakpoint in the setOnMouseMoved() I've examined the preceding stackframes to try to find the hit-detection code.
Used JNA to test different styles such as WS_EX_TRANSPARENT and WS_EX_LAYERED. Interestingly WS_EX_TRANSPARENT made the window fully mouse transparent - no mouse events over the painted pixels.
Tried putting the mouse listener on the rectangle/StackPane instead - no difference
MSDN article Layered Windows hints at this functionality being part of Windows rather than JavaFX. If this is true is there any workaround?
Hit testing of a layered window is based on the shape and transparency
of the window. This means that the areas of the window that are
color-keyed or whose alpha value is zero will let the mouse messages
through. If the layered window has the WS_EX_TRANSPARENT extended
window style, the shape of the layered window will be ignored and the
mouse events will be passed to the other windows underneath the
layered window.
In summary only known solution is to set the background to be "not quite" transparent to fool JavaFX into sending events.
rectangle.setFill(Color.rgb(0, 0, 0, 1d / 255d)); // receives mouse events

Categories

Resources