JavaFX Application unresponsive after drawing multiple canvas - java

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();
}
}
}
}

Related

How to change between wireframe and solid in javafx

3d software allow user to change draw mode dinamically. It can be implemented on javafx ?
Changing draw mode with radio buttons
In this approach a Box instance change its DrawMode with radiobuttons.
This is a single class javafx you can try .
App.java
public class App extends Application {
#Override
public void start(Stage stage) {
var perspective = new PerspectiveCamera(true);
perspective.setNearClip(0.1);
perspective.setFarClip(500);
perspective.setTranslateZ(-150);
Shape3D cube = new Box(50, 50, 50);
cube.setCullFace(CullFace.NONE);
cube.setMaterial(new PhongMaterial(Color.CORAL));
var toggleGroup = new ToggleGroup();
var solid = new RadioButton("solid");
solid.setToggleGroup(toggleGroup);
solid.setSelected(true);
var wire = new RadioButton("wireframe");
wire.setToggleGroup(toggleGroup);
var hBox = new HBox(solid, wire);
toggleGroup.selectedToggleProperty().addListener((o) -> {
Toggle selectedToggle = toggleGroup.getSelectedToggle();
if (selectedToggle == solid) {
cube.setDrawMode(DrawMode.FILL);
}
if (selectedToggle == wire) {
cube.setDrawMode(DrawMode.LINE);
}
});
var group3d = new Group(perspective, cube);
var subscene = new SubScene(group3d, 300, 400, true, SceneAntialiasing.BALANCED);
subscene.setCamera(perspective);
var stack = new StackPane(subscene, hBox);
stage.setScene(new Scene(stack, 300, 400));
stage.show();
}
public static void main(String[] args) {
launch();
}
}

How to make a JavaFX TRANPARENT/UNDECORATED Window draggable by Controller?

I´m trying to make my JavaFX transparent Sub-Window draggable by Controller (and FXML).
I already archieved it to make it draggable by AnotherWindow.java like that:
public class AnotherWindow {
double yOffset; double xOffset;
Stage anotherStage = new Stage();
public AnotherWindow(String fxml) {
try {
Parent root = (Parent) FXMLLoader.load(getClass().getResource(fxml));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("../application/application.css").toExternalForm());
scene.setOnMousePressed(event -> {
if(event.)
xOffset = anotherStage.getX() - event.getScreenX();
yOffset = anotherStage.getY() - event.getScreenY();
});
scene.setOnMouseDragged(event -> {
anotherStage.setX(event.getScreenX() + xOffset);
anotherStage.setY(event.getScreenY() + yOffset);
});
anotherStage.setResizable(false);
anotherStage.setAlwaysOnTop(true);
anotherStage.setScene(scene);
anotherStage.setTitle("Control Panel(AutoClicker)");
anotherStage.getScene().setFill(Color.TRANSPARENT);
anotherStage.initStyle(StageStyle.TRANSPARENT);
anotherStage.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The reason why I want to make it draggable by Controller, is that I want to move the Window by dragging a button and not by dragging the whole scene/window.
Hope you can help me! (sorry for my bad english, I´m quite young)
~Henri

JavaFX output to the SPI bus instead of HDMI on a RPi

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.

Libgdx images from pixmaps are drawn solid black

For startes, i know libgdx is primarily used for gaming. I have just found out about it and wanted to try to give it an extra other (possible) purpose, for example a simple photo frame. The below code is just a part of a proof of concept and when it runs as it should evolve to a bigger app.
Below i have posted a very simple class at what i'm up to now. What it does is every [num] seconds, in a different thread, it loads an image from disk, put it in a pixmap and creates a texture from it on the GL thread (if i understand everything correctly).
I came to this code after a lot of trial and error, It took me an hour to found out that a texture should be created in the OpenGL thread. When the texture is created outside the thread the images where just big black boxes without the loaded texture.
Well, when i ran this class version with textures created on the thread I finally see the image showing, and nicely fading every [num] seconds.
But, after 15 executions the images are starting to appear as black boxes again as if the texture is created outside the GL thread. I'm not getting any exceptions printed in the console.
The application is running on a Raspberry Pi with memory split 128/128. The images are jpeg images in 1920*1080 (not progressive). Memory usage is as follows according to top:
VIRT: 249m
RES: 37m
SHR: 10m
Command line is: java -Xmx128M -DPI=true -DLWJGJ_BACKEND=GLES -Djava.library.path=libs:/opt/vc/lib:. -classpath *:. org.pidome.raspberry.mirrorclient.BootStrapper
I see the RES rising when a new image is loaded but after loading it is back to 37.
The System.out.println("Swap counter: " +swapCounter); keeps giving me output when the thread is run.
Could one of you guys point me in the right direction into solving the issue that after 15 iterations the textures are not shown anymore and the images are solid black?
Here is my current code (name PhotosActor is misleading, a result of first trying it to be an Actor):
public class PhotosActor {
List<Image> images = new ArrayList<>();
private String imgDir = "appimages/photos/";
List<String> fileSet = new ArrayList<>();
private final ScheduledExecutorService changeExecutor = Executors.newSingleThreadScheduledExecutor();
Stage stage;
int swapCounter = 0;
public PhotosActor(Stage stage) {
this.stage = stage;
}
public final void preload(){
loadFileSet();
changeExecutor.scheduleAtFixedRate(switchimg(), 10, 10, TimeUnit.SECONDS);
}
private Runnable switchimg(){
Runnable run = () -> {
try {
swapCounter++;
FileInputStream input = new FileInputStream(fileSet.get(new Random().nextInt(fileSet.size())));
Gdx2DPixmap gpm = new Gdx2DPixmap(input, Gdx2DPixmap.GDX2D_FORMAT_RGB888);
input.close();
Pixmap map = new Pixmap(gpm);
Gdx.app.postRunnable(() -> {
System.out.println("Swap counter: " +swapCounter);
Texture tex = new Texture(map);
map.dispose();
Image newImg = new Image(tex);
newImg.addAction(Actions.sequence(Actions.alpha(0),Actions.fadeIn(1f),Actions.delay(5),Actions.run(() -> {
if(images.size()>1){
Image oldImg = images.remove(1);
oldImg.getActions().clear();
oldImg.remove();
}
})));
images.add(0,newImg);
stage.addActor(newImg);
newImg.toBack();
if(images.size()>1){ images.get(1).toBack(); }
});
} catch (Exception ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
};
return run;
}
private void loadFileSet(){
File[] files = new File(imgDir).listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println("Loading: " + imgDir + file.getName());
fileSet.add(imgDir + file.getName());
}
}
}
}
Thanks in advance and cheers,
John.
I was able to resolve this myself, A couple of minutes ago it struck me that i have to dispose the texture. I was in the believe that removing the image also removed the texture. Which it clearly did not (or i have to update to more recent version).
So what i did was create a new class extending the image class:
public class PhotoImage extends Image {
Texture tex;
public PhotoImage(Texture tex){
super(tex);
this.tex = tex;
}
public void dispose(){
try {
this.tex.dispose();
} catch(Exception ex){
System.out.println(ex.getMessage());
}
}
}
On all the location i was refering to the image class i changed it to this PhotoImage class. The class modified some now looks like:
public class PhotosActor {
List<PhotoImage> images = new ArrayList<>();
private String imgDir = "appimages/photos/";
List<String> fileSet = new ArrayList<>();
private final ScheduledExecutorService changeExecutor = Executors.newSingleThreadScheduledExecutor();
Stage stage;
int swapCounter = 0;
public PhotosActor(Stage stage) {
this.stage = stage;
}
public final void preload(){
loadFileSet();
changeExecutor.scheduleAtFixedRate(switchimg(), 10, 10, TimeUnit.SECONDS);
}
private Runnable switchimg(){
Runnable run = () -> {
try {
swapCounter++;
byte[] byteResult = readLocalRandomFile();
Pixmap map = new Pixmap(byteResult, 0, byteResult.length);
Gdx.app.postRunnable(() -> {
System.out.println("Swap counter: " +swapCounter);
Texture tex = new Texture(map);
map.dispose();
PhotoImage newImg = new PhotoImage(tex);
images.add(0,newImg);
stage.addActor(newImg);
addTransform(newImg);
});
} catch (Exception ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
};
return run;
}
public void addTransform(Image img){
switch(new Random().nextInt(3)){
case 0:
img.toBack();
if(images.size()>1){ images.get(1).toBack(); }
img.addAction(Actions.sequence(Actions.alpha(0),Actions.fadeIn(1f),Actions.delay(5),Actions.run(() -> {
removeOldImg();
})));
break;
case 1:
img.toBack();
if(images.size()>1){ images.get(1).toBack(); }
img.setPosition(1920f, 1080f);
img.addAction(Actions.sequence(Actions.moveTo(0f, 0f, 5f),Actions.run(() -> {
removeOldImg();
})));
break;
case 2:
img.toBack();
if(images.size()>1){ images.get(1).toBack(); }
img.setScale(0f, 0f);
img.setPosition(960f, 540f);
img.addAction(Actions.sequence(Actions.parallel(Actions.scaleTo(1f, 1f, 5f), Actions.moveTo(0f, 0f, 5f)),Actions.run(() -> {
removeOldImg();
})));
break;
}
}
private void removeOldImg(){
if(images.size()>1){
PhotoImage oldImg = images.remove(1);
oldImg.remove();
oldImg.getActions().clear();
oldImg.dispose();
}
System.out.println("Amount of images: " + images.size());
}
private byte[] readLocalRandomFile() throws Exception{
FileInputStream input = null;
try {
input = new FileInputStream(fileSet.get(new Random().nextInt(fileSet.size())));
ByteArrayOutputStream out;
try (InputStream in = new BufferedInputStream(input)) {
out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n = 0;
while (-1 != (n = in.read(buf))) {
out.write(buf, 0, n);
}
out.close();
return out.toByteArray();
} catch (IOException ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(PhotosActor.class.getName()).log(Level.SEVERE, null, ex);
}
throw new Exception("No data");
}
private void loadFileSet(){
File[] files = new File(imgDir).listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println("Loading: " + imgDir + file.getName());
fileSet.add(imgDir + file.getName());
}
}
}
}
In the remove function i now have added
oldImg.dispose();
to get rid of the texture. Image transitions are now happy running on 50+ fps on the Raspberry Pi and the image rotation counter is on: 88 now. If there where people thinking thanks for your time!

Flickering XYImageAnnotation on JFreeChart with timer

I have difficulties drawing an image on a JFreeChart - XYLineChart. The main problem is the x and y coordinates of the annotation is updated dynamically in real time.So with my code adding the annotation and clearing it for the new one to be drawn causes flickering which is annoying for the user.
I have checked some examples of flickering problems on JAVA using update() , paint () or repaint() methods using graphics but seems not implementable on a JFreeChart.
Do you have any ideas how to get rid of the flicker or a workaround to use one bufferedImage on the JFreeChart instead of an annotation ?
To be more specific here is the drawn line and the image :
Screenshot
So this cross hair (as the buffered image) should go on the plot line up and down with the updated values of x and y axis.But this motion causes the flickering unfortunately.
Here is the part of my code where I draw the image - I cannot provide SSCCE I guess since there are more than 15 classes and 5k of written code :
// After a button clicked on panel
SomeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// The chart and XYPlot is already drawn at this point
// Reading the image
try {
myPicture = ImageIO
.read(new File("\\\\Users2\\blabla\\Data\\MyPictures\\x.png"));
} catch (IOException e) {
e.printStackTrace();
}
// Setting up a timer
timer2 = new java.util.Timer();
Object source = event.getSource();
if (source == SomeButton) {
// Setting up a task
task2 = new java.util.TimerTask() {
#Override
public void run() {
double x1;
double y1;
try {
// Getting different x and y values from a microcontroller instantaneously
if (microContConnected()) {
x1 = microCont.getX();
y1 = microCont.getY();
// creating the annotation
XYImageAnnotation ImageAnn = new XYImageAnnotation(x1, y1, myPicture);
// Here is the drawing and clearing made !
plot.addAnnotation(ImageAnn);
pause(50);
plot.clearAnnotations();
}
} catch (SerialPortException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
timer2.scheduleAtFixedRate(task2, 50, 50);
}
}
});
It seems I found a solution myself ; instead of adding the image to plot I use the renderer and there is no pause function between adding and removing the picture with new coordinates.. also sequence of adding and removed are reversed. Surprising for me to work this way I must say. There is no flickering left; it's as smooth as a clipped graphics or double buffered. :) Here is the new code :
// renderer
final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
// Reading the image
try {
myPicture = ImageIO.read(new File("\\\\Users2\\blabla\\Data\\MyPictures\\x.png"));
} catch (IOException e) {
e.printStackTrace();
}
// Setting up a timer
timer2 = new java.util.Timer();
Object source = event.getSource();
if (source == someButton) {
task2 = new java.util.TimerTask() {
#Override
public void run() {
if (check == true) {
if (microContConnected()) {
x1 = microCont.getX();
y1 = microCont.getY();
renderer.removeAnnotations();
XYImageAnnotation img2 = new XYImageAnnotation(
x1, y1, myPicture);
renderer.addAnnotation(img2,
Layer.FOREGROUND);
}
}
}
};
timer2.scheduleAtFixedRate(task2, 50, 50);
}

Categories