I am experimenting with a JavaFX application on a raspberry. It works great on my HDMI monitor, and the rendered size seems all ok.
However, I now have added my 3.5 inch touch screen running on the SPI interface but I still get the JavaFX output on the HDMI monitor. It works ok for the graphical interface. The console appears on the 3.5 inch LCD, so I guess I need to set a setting somewhere for JavaFX to also output the 3.5 LCD, but I cannot remember how to do this.
[Edit:]
This is my (shortened) application code:
public class Main extends Application {
private static int width = 480;
private static int height = 320;
static final Text statusText = new Text(25, 300, "(no status update)");
static final Label messagesArea = new Label("");
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Registration UHF scanner");
final Group rootGroup = new Group();
final Scene scene = new Scene(rootGroup, width, height, Color.BLACK);
final Text title = new Text(25, 45, "Youth 2000 UHF Registration");
title.setFill(Color.WHITESMOKE);
title.setFont(Font.font(java.awt.Font.SERIF, FontWeight.BOLD, 25));
rootGroup.getChildren().add(title);
messagesArea.setWrapText(true);
messagesArea.setStyle("-fx-font-family: \"Comic Sans MS\"; -fx-font-size: 16; -fx-text-fill: white;");
messagesArea.setTranslateX(25);
messagesArea.setTranslateY(35);
rootGroup.getChildren().add(messagesArea);
statusText.setFill(Color.WHITESMOKE);
statusText.setFont(Font.font(java.awt.Font.SERIF, 16));
rootGroup.getChildren().add(statusText);
scene.addEventFilter(KeyEvent.ANY, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent keyEvent) {
System.out.println("Key pressed: " + keyEvent.getCode());
if (keyEvent.getCode() == KeyCode.ENTER) {
System.out.println("Test!");
}
}
});
Button shutDown = new Button("Shutdown");
shutDown.setPrefWidth(110);
shutDown.setPrefHeight(10);
shutDown.setTranslateX(width - 115);
shutDown.setTranslateY(height - 35);
rootGroup.getChildren().add(shutDown);
Button restart = new Button("Restart");
restart.setPrefWidth(110);
restart.setPrefHeight(10);
restart.setTranslateX(width - 115);
restart.setTranslateY(height - 75);
rootGroup.getChildren().add(restart);
shutDown.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
// Shutdown the computer.
try {
shutDown();
} catch (Exception ex){
System.out.println("Problem shutting down: " + ex.getMessage());
setStatusText("Problem shutting down!!!!");
}
}
});
restart.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
// Shutdown the computer.
try {
restartServer();
} catch (Exception ex){
System.out.println("Problem restarting: " + ex.getMessage());
setStatusText("Problem restarting!!!!");
}
}
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
System.out.println("Starting system...");
if (System.getProperty("screenWidth") != null) {
width = Integer.valueOf(System.getProperty("screenWidth"));
}
if (System.getProperty("screenHeight") != null) {
height = Integer.valueOf(System.getProperty("screenHeight"));
}
Application.launch(args);
}
}
There is a need to mirror the FrameBuffer to the SPI.
fbcp: https://github.com/tasanakorn/rpi-fbcp
The main display has to be configured correctly to have the same resolution. So in /boot/config.txt. In my case:
disable_overscan=1
framebuffer_width=480
framebuffer_height=320
#set specific CVT mode
hdmi_cvt 480 320 60 1 0 0 0
#set CVT as default
hdmi_group=2
hdmi_mode=87
hdmi_force_hotplug=1
That only left me with a little problem left with the touch screen as the xy are swapped. This should be able to be fixed by changing the
swap_xy=X
parameter in /etc/modules but for me that hasn't worked so far. I do want to use the screen in landscape, so rotating the image is not really an option.
Related
I have an application where I want to show a FlowPane with PDF pages thumbnails drawn into canvases. I'm using PDFBox and FXGraphics2D to render pages.
My current implementation creates a number of canvases as the number of pages, adds them to the FlowPane and then spins a number or async tasks to draw the pages content into the canvas.
I'm not sure if the async drawing is the recommended way but the idea is to not use the JavaFX thread to do the PDF parsing to avoid freezing the application.
Now, my issue is this, I can see from the logs that all the rendering tasks have finished and the document is closed. The UI shows some rendered pages but it stays unresponsive for ~10 seconds. After that the application revives, all pages are rendered and everything works nicely.
I tried to profile and I think this is the relevant part:
But I have limited knowledge of what is going on under the hood and I couldn't figure out what I'm doing wrong. Do you have an idea or hint on what is wrong with my approach/code and how it can be improved? Ideally I'd like to have the application fast and responsive while the pages thumbnails are filled.
I'm on Linux with a pretty decent machine but I also tested on Windows and got the same behavior. I also tried to replace FlowPane with HBox or VBox but still the same happened.
Here is some ugly code to reproduce the behavior:
public class TestApp extends Application {
public static void main(String[] args) {
Application.launch(TestApp.class, args);
}
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
var flow = new FlowPane();
flow.setOnDragOver(e -> {
if (e.getDragboard().hasFiles()) {
e.acceptTransferModes(TransferMode.COPY);
}
e.consume();
});
flow.setOnDragDropped(e -> {
var tasks = new ArrayList<TestApp.RenderTask>();
try {
var document = PDDocument.load(e.getDragboard()
.getFiles().get(0));
var renderer = new PDFRenderer(document);
for (int i = 1; i <= document.getNumberOfPages(); i++) {
var page = document.getPage(i - 1);
var cropbox = page.getCropBox();
var thumbnailDimension = new Dimension2D(400,
400 * (cropbox.getHeight() / cropbox.getWidth()));
var thumbnail = new Canvas(thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
var gs = thumbnail.getGraphicsContext2D();
var pop = gs.getFill();
gs.setFill(Color.WHITE);
gs.fillRect(0, 0, thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
gs.setFill(pop);
tasks.add(new TestApp.RenderTask(renderer, thumbnail, i));
flow.getChildren().add(new Group(thumbnail));
}
var exec = Executors.newSingleThreadExecutor();
tasks.forEach(exec::submit);
exec.submit(()-> {
try {
document.close();
System.out.println("close");
} catch (IOException ioException) {
ioException.printStackTrace();
}
});
} catch (Exception ioException) {
ioException.printStackTrace();
}
e.setDropCompleted(true);
e.consume();
});
var scroll = new ScrollPane(flow);
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
root.setCenter(scroll);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private static record RenderTask(PDFRenderer renderer, Canvas canvas, int page) implements Runnable {
#Override
public void run() {
var gs = new FXGraphics2D(canvas.getGraphicsContext2D());
gs.setBackground(WHITE);
try {
renderer.renderPageToGraphics(page - 1, gs);
} catch (IOException e) {
e.printStackTrace();
}
gs.dispose();
}
}
}
And this is the 310 pages PDF file I'm using to test it.
Thanks
I finally managed to get what I wanted, the application responds, the thumbnails are populated as they are ready and the memory usage is limited.
The issue
I thought I was drawing the canvases off of the FX thread but how Canvas works is that you fill a buffer of drawing instructions and they are executed when the canvas becomes part of the scene (I think). What was happening is that I was quickly filling the canvases with a lot of drawing instructions, adding all the canvases to the FlowPane and the the application was spending a lot of time actually executing the drawing instructions for 310 canvas and becoming unresponsive for ~10 seconds.
After some reading and suggestions from the JavaFX community here and on Twitter, I tried to switch to an ImageView implementation, telling PDFBox to create a BufferedImage of the page and use it to create an ImageView. It worked nicely but memory usage was 10 times compared to the Canvas impl:
The best of both worlds
In my final solution I add white canvases to the FlowPane, create a BufferedImage of the page and convert it to an Image off of the FX thread, draw the image to the Canvas and discard the Image.
This is memory usage for the same 310 pages PDF file:
And this is the application responsiveness:
EDIT (added code to reproduce)
Here the code I used (with SAMBox instead of PDFBox)
public class TestAppCanvasImageMixed extends Application {
public static void main(String[] args) {
Application.launch(TestAppCanvas.class, args);
}
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
var button = new Button("render");
button.setDisable(true);
root.setTop(new HBox(button));
var flow = new FlowPane();
flow.setOnDragOver(e -> {
if (e.getDragboard().hasFiles()) {
e.acceptTransferModes(TransferMode.COPY);
}
e.consume();
});
flow.setOnDragDropped(e -> {
ArrayList<TestAppCanvasImageMixed.RenderTaskToImage> tasks = new ArrayList<>();
try {
var document = PDDocument.load(e.getDragboard()
.getFiles().get(0));
var renderer = new PDFRenderer(document);
for (int i = 1; i <= document.getNumberOfPages(); i++) {
var page = document.getPage(i - 1);
var cropbox = page.getCropBox();
var thumbnailDimension = new Dimension2D(400,
400 * (cropbox.getHeight() / cropbox.getWidth()));
var thumbnail = new Canvas(thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
var gs = thumbnail.getGraphicsContext2D();
var clip = new Rectangle(thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
clip.setArcHeight(15);
clip.setArcWidth(15);
thumbnail.setClip(clip);
var pop = gs.getFill();
gs.setFill(WHITE);
gs.fillRect(0, 0, thumbnailDimension.getWidth(), thumbnailDimension.getHeight());
gs.setFill(pop);
var g = new Group();
tasks.add(new TestAppCanvasImageMixed.RenderTaskToImage(renderer, 400 / cropbox.getWidth(), i, thumbnail,
() -> g.getChildren().setAll(thumbnail)));
flow.getChildren().add(new Group());
}
button.setOnAction(a -> {
var exec = Executors.newFixedThreadPool(1);
tasks.forEach(exec::submit);
exec.submit(() -> {
try {
document.close();
System.out.println("close");
} catch (IOException ioException) {
ioException.printStackTrace();
}
});
});
button.setDisable(false);
} catch (Exception ioException) {
ioException.printStackTrace();
}
e.setDropCompleted(true);
e.consume();
});
var scroll = new ScrollPane(new StackPane(flow));
scroll.setFitToHeight(true);
scroll.setFitToWidth(true);
root.setCenter(scroll);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private static record RenderTaskToImage(PDFRenderer renderer, float scale, int page, Canvas canvas, Runnable r) implements Runnable {
#Override
public void run() {
try {
var bufferedImage = renderer.renderImage(page - 1, scale, ImageType.ARGB, RenderDestination.VIEW);
var image = SwingFXUtils.toFXImage(bufferedImage, null);
var gc = canvas.getGraphicsContext2D();
gc.drawImage(image, 0, 0);
Platform.runLater(() -> r.run());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 3 years ago.
I'm trying to create a simple game but I'm already failing with the Background.
My code works only for a couple of seconds before I'm getting this error:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at javafx.scene.Parent.updateCachedBounds(Parent.java:1592)
at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
at javafx.scene.Node.updateGeomBounds(Node.java:3579)
at javafx.scene.Node.getGeomBounds(Node.java:3532)
at javafx.scene.Node.getLocalBounds(Node.java:3480)
at javafx.scene.Node.updateTxBounds(Node.java:3643)
at javafx.scene.Node.getTransformedBounds(Node.java:3426)
at javafx.scene.Node.updateBounds(Node.java:559)
at javafx.scene.Parent.updateBounds(Parent.java:1719)
at javafx.scene.Parent.updateBounds(Parent.java:1717)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2404)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$29(Toolkit.java:398)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$403(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$null$48(GtkApplication.java:139)
at java.lang.Thread.run(Thread.java:748)
I tried finding it out on my own, but I have no idea.
Here's my code:
public class RocketMain extends Application{
int width = 640;
int height = 1000;
Pane root;
Stars stars;
public static void main(String[] args){
Application.launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
root = new Pane();
Scene sc = new Scene(root, width, height);
primaryStage.setScene(sc);
primaryStage.setResizable(false);
primaryStage.centerOnScreen();
stars = new Stars(width, height);
new Thread(() -> stars.createStars()).start();
new Thread(() -> update()).start();
root.getChildren().add(stars);
primaryStage.show();
primaryStage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
}
public void update(){
while(true){
Platform.runLater(() -> {
stars.getChildren().removeAll(stars.getChildren());
stars.getChildren().addAll(stars.stars);
root.getChildren().removeAll(root.getChildren());
root.getChildren().add(stars);
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The Stars Class which keeps spawning new sprites and moves/deletes them when offscreen:
public class Stars extends Pane {
public ArrayList<Rectangle> stars;
ArrayList<Rectangle> rem;
double width, height;
public Stars(int width, int height){
stars = new ArrayList<>();
rem = new ArrayList<>();
this.width = width;
this.height = height;
}
public void createStars(){
int slowing = 3;
while(true){
if (Math.random() < (1.0/slowing)){
int size = (int) Math.floor(Math.random()*8);
Rectangle temp = new Rectangle(Math.random() * width, -10, size, size);
temp.setFill(Color.BLACK);
this.stars.add(temp);
Platform.runLater(() -> {
this.getChildren().remove(temp);
this.getChildren().add(temp);
});
}
for (Rectangle r : stars) {
r.setY(r.getY() + r.getHeight() / slowing);
if (r.getY() > height / 2) {
rem.add(r);
}
}
stars.removeAll(rem);
rem.clear();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
tia I hope this was understandable
You seem to have moved almost all code that updates from background threads nodes that are part of a scene into Platform.runLater calls. The stacktrace indicates this is the case though.
You've missed one place where updates happen though:
r.setY(r.getY() + r.getHeight() / slowing);
in the enhanced for loop inside createStars.
However there's another issue that could cause similar issues, but the stacktrace indicates that in this run this was not the case:
You update the lists from the background thread, but read them from the javafx application thead. There is no synchronisation done for this though which could result in one of the threads accessing a list that is in an inconsistent state.
However your logic is more complicated that it needs to be. The code you run in every loop iteration doesn't take long to execute, if we ignore Thread.sleep. JavaFX provides you with a class that allows you to schedule regularly running logic on the JavaFX application thread which I recommend using (Timeline).
public class Stars extends Pane {
double width, height;
private final Timeline timeline;
public void startAnimation() {
timeline.play();
}
public Stars(int width, int height){
timeline = new Timeline(new KeyFrame(Duration.millis(20), new EventHandler<ActionEvent>() {
private int iteration = 0;
private final Random random = new Random();
private final List<Rectangle> stars = new LinkedList<>();
private final Set<Rectangle> toRemove = new HashSet<>();
#Override
public void handle(ActionEvent event) {
// iteration is 0 on every 5th iteration to simulate both background threads at once
iteration = (iteration + 1) % 5;
final int slowing = 3;
if (random.nextInt(slowing) == 0) { // also true in 1 of 3 cases
int size = random.nextInt(8);
Rectangle temp = new Rectangle(random.nextDouble() * Stars.this.width, -10, size, size);
temp.setFill(Color.BLACK);
this.stars.add(temp);
getChildren().add(temp);
}
Iterator<Rectangle> iter = stars.iterator();
while (iter.hasNext()) {
Rectangle r = iter.next();
if (r.getY() > Stars.this.height / 2) {
// move rect list to set
toRemove.add(r);
iter.remove();
}
}
if (iteration == 0) {
// logic from background thread with lower frequency
getChildren().removeAll(toRemove);
toRemove.clear();
}
}
}));
timeline.setCycleCount(Animation.INDEFINITE);
this.width = width;
this.height = height;
}
}
This way all you need to do is call startAnimation, no threads needed, no issues with concurrency.
Further notes:
list.removeAll(list);
should be changed to
list.clear();
Since you follow this up by
list.addAll(list2);
you can also replace both lines with
list.setAll(list2);
(ObservableList provides this functionality)
I have strange problems with JavaFX that look a lot like a bug. I want to do the following:
Entering fullscren when starting my application
Press escape to exit the application (not fullscreen, the entire application)
So far, I have the following code:
public class AppTest extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
stage.setFullScreenExitHint("Press ESCAPE to exit");
stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
stage.setFullScreen(true);
Rectangle2D screenBounds = Screen.getPrimary().getBounds();
stage.setX(screenBounds.getMinX());
stage.setY(screenBounds.getMinY());
double screenWidth = screenBounds.getWidth();
double screenHeight = screenBounds.getHeight();
stage.setWidth(screenWidth);
stage.setHeight(screenHeight);
Group root = new Group();
Scene scene = new Scene(root);
stage.setScene(scene);
scene.setOnKeyTyped(event -> {
if(event.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
Canvas canvas = new Canvas(screenWidth, screenHeight);
root.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.BLUE);
gc.fillRect(0,0, screenWidth, screenHeight);
stage.show();
}
}
I'm on macOS.
In general it goes fullscreen. I'm saying in general because the real version of this code doesn't always. Sometimes, it is just a maximized window.
Then, when pressing escape, I get a maximized window instead of exiting the application.
How can I fix that?
Change:
scene.setOnKeyTyped
To:
scene.setOnKeyReleased
This explains why.
guys! I am having an issue in my JavaFX Application. Images are able to be dragged and placed anywhere on the entire pane. This was working wonderfully, up until I added a second event handler. I added a second event handler to handle deletion. Right-clicking on the image gives you a pop up menu with deletion as an option. As soon as I added this, dragging stopped working properly. While it still works somewhat, the mouse is no longer ever on the image. As soon as you begin dragging, the image jumps over and down like 300 px? I am still able to drag, but I am not dragging on top of the image.
I understand this explanation isn't clear, and I can easily upload a gif of what happens if someone wants me to. Here's the code:
#FXML
private void handleImageButton()
{
System.out.println("Image Clicked");
closeMenus(6);
//contains the image path or image URL
String picURL = imageURL.getText();
if (!picURL.equals(""))
{
Image image = new Image(new File(picURL).toURI().toString());
ImageView iv = new ImageView();
iv.setImage(image);
iv.setCursor(Cursor.MOVE);
iv.setOnMousePressed(imageOnMousePressedEventHandler);
iv.setOnMouseDragged(imageOnMouseDraggedEventHandler);
workspace.getChildren().add(iv);
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
contextMenu.getItems().addAll(delete);
iv.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event){
workspace.getChildren().remove(iv);
}
});
} //end if
}
});
}
}
EventHandler<MouseEvent> imageOnMousePressedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (ImageTools.isVisible())
{
originalX = t.getSceneX();
originalY = t.getSceneY();
newX = ((ImageView)(t.getSource())).getTranslateX();
newY = ((ImageView)(t.getSource())).getTranslateY();
System.out.println("(" + newX + ", " + newY + ")");
}
}
};
EventHandler<MouseEvent> imageOnMouseDraggedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (ImageTools.isVisible())
{
double offsetX = t.getSceneX() - originalX;
double offsetY = t.getSceneY() - originalY;
double newTranslateX = newX + offsetX;
double newTranslateY = newY + offsetY;
((ImageView)(t.getSource())).setTranslateX(newTranslateX);
((ImageView)(t.getSource())).setTranslateY(newTranslateY);
System.out.println("(" + newTranslateX + ", " + newTranslateY + ")");
}
}
};
The problem is that you are overriding your mouse listeners:
iv.setOnMousePressed(imageOnMousePressedEventHandler);
iv.setOnMouseDragged(imageOnMouseDraggedEventHandler);
iv.setOnMousePressed(new EventHandler() {});
setOnXYZ is a setter for a property, holding only one listener, so you are effectively overwriting your listeners in steps 1 and 3.
Use Node.addEventHandler() to add multiple handlers for the same EventType or in this case i would suggest using setOnContextMenuRequested to show the context menu because it is more convenient and system independent.
i am showing simple code sample. I showed an gif image in a Jlabel. When run the programme, TASK manager shows that memory is increasing continuously. Why it happens?
Edited:
try this code please... on show glass button, glass panel is shown with gif image and a hide glass button in it and with that memory will be started increasing. On clicking hide glass button, glass panel will be hidden and memory increasing will b stopped.
#mKorbel : I had debugged it, the constructor will be called once, so no re-initializing of JFrame and also included : setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
public class circle extends JFrame {
public ImageIcon pic;
final JPanel glass;
public JButton glass_show;
public JButton hide_glass;
public circle() {
super("Hi shamansdsdsd");
setSize(500, 300);
// Image icon initialize once :
pic = new ImageIcon("images/loadinag.gif");
glass_show = new JButton("Show Glass panel");
this.add(glass_show);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
glass = (JPanel) this.getGlassPane();
hide_glass = new JButton("Hide Glass panel");
glass.add(hide_glass);
glass.add(new JLabel(pic));
glass.setOpaque(false);
}
public void initialize_listeners(){
glass_show.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent A) {
glass.setVisible(true);
}
});
hide_glass.addActionListener(new ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent A) {
glass.setVisible(false);
}
});
}
public static void main(String[] args) {
circle mFrame = new circle();
mFrame.initialize_listeners();
mFrame.setVisible(true);
}
}
There is a bug in Java with animated GIF images. There is no memory increase with other images.
Edit;
Below example runs without memory leak; but you need Eclipse SWT library from Eclipse's site
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
public class GIFExample {
static Display display;
static Shell shell;
static GC shellGC;
static Color shellBackground;
static ImageLoader loader;
static ImageData[] imageDataArray;
static Thread animateThread;
static Image image;
static final boolean useGIFBackground = false;
public static void main(String[] args) {
display = new Display();
shell = new Shell(display);
shell.setSize(300, 300);
shell.open();
shellGC = new GC(shell);
shellBackground = shell.getBackground();
FileDialog dialog = new FileDialog(shell);
dialog.setFilterExtensions(new String[] {"*.gif"});
String fileName = dialog.open();
if (fileName != null) {
loader = new ImageLoader();
try {
imageDataArray = loader.load(fileName);
if (imageDataArray.length > 1) {
animateThread = new Thread("Animation") {
#Override
public void run() {
/* Create an off-screen image to draw on, and fill it with the shell background. */
Image offScreenImage = new Image(display, loader.logicalScreenWidth, loader.logicalScreenHeight);
GC offScreenImageGC = new GC(offScreenImage);
offScreenImageGC.setBackground(shellBackground);
offScreenImageGC.fillRectangle(0, 0, loader.logicalScreenWidth, loader.logicalScreenHeight);
try {
/* Create the first image and draw it on the off-screen image. */
int imageDataIndex = 0;
ImageData imageData = imageDataArray[imageDataIndex];
if (image != null && !image.isDisposed()) image.dispose();
image = new Image(display, imageData);
offScreenImageGC.drawImage(
image,
0,
0,
imageData.width,
imageData.height,
imageData.x,
imageData.y,
imageData.width,
imageData.height);
/* Now loop through the images, creating and drawing each one
* on the off-screen image before drawing it on the shell. */
int repeatCount = loader.repeatCount;
while (loader.repeatCount == 0 || repeatCount > 0) {
switch (imageData.disposalMethod) {
case SWT.DM_FILL_BACKGROUND:
/* Fill with the background color before drawing. */
Color bgColor = null;
if (useGIFBackground && loader.backgroundPixel != -1) {
bgColor = new Color(display, imageData.palette.getRGB(loader.backgroundPixel));
}
offScreenImageGC.setBackground(bgColor != null ? bgColor : shellBackground);
offScreenImageGC.fillRectangle(imageData.x, imageData.y, imageData.width, imageData.height);
if (bgColor != null) bgColor.dispose();
break;
case SWT.DM_FILL_PREVIOUS:
/* Restore the previous image before drawing. */
offScreenImageGC.drawImage(
image,
0,
0,
imageData.width,
imageData.height,
imageData.x,
imageData.y,
imageData.width,
imageData.height);
break;
}
imageDataIndex = (imageDataIndex + 1) % imageDataArray.length;
imageData = imageDataArray[imageDataIndex];
image.dispose();
image = new Image(display, imageData);
offScreenImageGC.drawImage(
image,
0,
0,
imageData.width,
imageData.height,
imageData.x,
imageData.y,
imageData.width,
imageData.height);
/* Draw the off-screen image to the shell. */
shellGC.drawImage(offScreenImage, 0, 0);
/* Sleep for the specified delay time (adding commonly-used slow-down fudge factors). */
try {
int ms = imageData.delayTime * 10;
if (ms < 20) ms += 30;
if (ms < 30) ms += 10;
Thread.sleep(ms);
} catch (InterruptedException e) {
}
/* If we have just drawn the last image, decrement the repeat count and start again. */
if (imageDataIndex == imageDataArray.length - 1) repeatCount--;
}
} catch (SWTException ex) {
System.out.println("There was an error animating the GIF");
} finally {
if (offScreenImage != null && !offScreenImage.isDisposed()) offScreenImage.dispose();
if (offScreenImageGC != null && !offScreenImageGC.isDisposed()) offScreenImageGC.dispose();
if (image != null && !image.isDisposed()) image.dispose();
}
}
};
animateThread.setDaemon(true);
animateThread.start();
}
} catch (SWTException ex) {
System.out.println("There was an error loading the GIF");
}
}
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
Code Source
there are two areas
1) you forgot to declare setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); then current JVM instance is still in (the Native OS) RAM, until PC restart or power-off
2) maybe you are create a new JFrame for every Images on the fly