I want to read a file to get some points and then draw these points on an image. Currently, I am able to draw values on the image, but the file is read three times and the rectangles are drawn three times. I don't know where is the problem. Below is the code. The Read() function works fine seperately so I didn't include it in the code.
P.S: I am beginner in JAVA and don't know much about JFrame and Jcomponent.
public class LoadImageApp extends JComponent {
BufferedImage img;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
Read(g);// This is the function in which I read a file.
}
public LoadImageApp() {
try {
img = ImageIO.read(this.getClass().getResource("/New York.jpg"));
} catch (IOException e) {
}
}
public Dimension getPreferredSize() {
if (img == null) {
return new Dimension(100,100);
} else {
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Load Image Sample");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
LoadImageApp img = new LoadImageApp();
f.add(img);
f.pack();
f.setVisible(true);
}
}
Do not, do not, DO NOT read from a file from within any painting method such as paintComponent(...). :)
That method should be for painting only. The more you slow it down, the less responsive your GUI will seem.
You cannot control how many times the method gets called, since it is not under direct control by you, the programmer.
You can't even control fully if the paintComponent method gets called, since the JVM might decide that too many requests for redraw are being stacked up, and it may not honor all of them.
Instead
read in the data once in a constructor or something similar.
I would create a read method that stores my points in an ArrayList<Point>, and then inside of the paintComponent method, iterate through that ArrayList using a for loop and draw them.
If the points don't change during your program's run, you could even draw them directly on to the BufferedImage by getting its Graphics context and using that to paint the points on to the image, and then show the new BufferedImage in your paintComponent method.
Other suggestions:
That empty catch block where you read your image is a dangerous thing to do. It's the coding equivalent of driving a motorcycle with your eyes closed. At least print out a stacktrace.
The WindowListener is not needed. Instead simply set your JFrame's defaultCloseOperation to JFrame.EXIT_ON_CLOSE: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Related
This is my first time trying to use another thread in java, could someone tell me how to make it work please? I've read others topics about it, but I didn't find a solution.
I'd like to draw a gif in another thread (drawn at at a random position and during the duration of its animation).
The problem is that drawImage() in the second thread just doesn't do anything. My counter works well (it prints 1.. 2.. 3 ...), but no image is drawn (or I can't see it).
The condition is false at the begining, then it is true at one moment (to create only one new thread and no more), then it is false again.
if (condition) {
(new ThreadGif(this,g)).start();
}
However when I remove the condition in paintComponent(), it draws something which means that drawImage() works. So when it creates lots of new threads, every image of the gif is drawn at a random location and it starts the gif again and again (and the counter(s) still work well).
This could be fine, but I don't think creating thousands of new thread is the answer : I just need one. And also, I need just one random position for each gif, not one different for each image of the gif.
I hope I've been clear enough. Please help me understand how to make it work :) Thank you very much.
Here are simplified versions of my two classes :
ThreadGif.java :
public class ThreadGif extends Thread {
Screen screen;
Graphics g;
boolean running = true;
public ThreadGif(Screen screen, Graphics g) {
this.g = g;
this.screen = screen;
}
public void run() {
int aleaX = new Random().nextInt(300)/100;
int aleaY = new Random().nextInt(300)/100;
int compt = 1;
while (running) {
g.drawImage(new ImageIcon("res/feu.gif").getImage(), screen.tailleCase*aleaX, screen.tailleCase*aleaY, screen.tailleCase*2, screen.tailleCase*2, screen);
System.out.println("thread " + compt);
compt++;
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
e.printStackTrace ();
}
}
}
}
Screen.java :
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
public Screen(Frame frame) {
this.frame = frame;
thread.start();
}
public void paintComponent(Graphics g) {
g.clearRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
if (condition) {
(new ThreadGif(this,g)).start();
}
}
public void run() {
while (running) {
repaint();
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
e.printStackTrace ();
}
}
System.exit(0);
}
}
Pretty simple: you mixed two approaches up.
The paintComponent-method launches a new ThreadGif every time it's called and ThreadGif itself paints within it's thread until it's terminated, but without refreshing the screen.
These two approaches combined might either result in strange behaviour, e.g. two images painted where pieces overlay each other, or the new ThreadGif simply renders the new image every-time you repaint the screen.
Solution: Start by assigning each class specific tasks, without mixing anything up, or splitting tasks between two classes. E.g.:
ThreadGif doesn't paint anything itself, but repaints the Screen. The Screen can request the image that should be displayed from ThreadGif.
Make ThreadGif an own Component that handles it's own rendering and ommit the Screen-class from painting anything.
Before you read, this will be informative: Java JFrame won't show up after using .setVisible(true) after being invisible
Hello I am working on a library API that let's you capture an area of the screen, and it returns you a class that contains the ByteArrayInputStream and utility methods like createBufferedImage, createFile, etc.
Basically you create a Bootstrap instance, and pass the capturer type you want as a dependency (ScreenshotCapturer or GifCapturer):
Bootstrap b = new Bootstrap(new ScreenshotCapturer());
And the beginCapture method receives an object that implements ScreenCaptureCallback which is the callback event that the captured result will be passed to.
This is a short background.
Now when you use the beginCapture method, basically what it does is creates new instance of SelectionCamera, this is basically the component that paints the selection area you're selecting when dragging the mouse, and updates the listeners.
once created instance, it calls super.setVisible(true);
After that method gets called, the frame will show up, and also show the old painted screen for like 600-500miliseconds, I am not exactly sure, but it disappears so quickly.
Take a look at this live example:
Note use the video option, otherwise you will not see what I'm seeing as gif is too slow to show it!
http://gyazo.com/d2f0432ada37842966b42dfd87be4240
You can see after I click Screenshot again, it shows the old selected area and disappears quickly. (by the way the frame you see in the gif is not part of the app, just dummy hello world example usage).
The process of image capture.
Step 1
beginCapture gets called:
public void beginCapture(final ScreenCaptureCallback c) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
capturer.setCallback(c);
capturer.beginSelection();
}
});
}
Step 2
beginSelection gets called in the Capturer class (ScreenshotCapturer extends Capturer (abstract)
#Override
public void beginSelection() {
super.init();
this.setHotkeys();
super.getCamera().startSelection();
}
Step 3
CaptureCamera#startSelection() gets called
public void startSelection() {
super.getContentPane().removeAll();
super.getContentPane().repaint();
super.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
this.selector = new SelectionCamera();
this.selectionMosueAdapter.updateCamera(this.selector);
this.selectionMouseMotion.updateCamera(this.selector);
super.add(this.selector);
super.setVisible(true);
super.repaint();
super.getContentPane().repaint();
}
Step 4
The user selects an area, and both mouse listener and mouse motion listens to it(Take a look at mouse motion):
#Override
public void mouseDragged(MouseEvent e) {
Point dragPoint = e.getPoint();
Point startPoint = this.selector.getStartPoint();
int x = Math.min(startPoint.x, dragPoint.x);
int y = Math.min(startPoint.y, dragPoint.y);
int width = Math.max(startPoint.x - dragPoint.x, dragPoint.x - startPoint.x);
int height = Math.max(startPoint.y - dragPoint.y, dragPoint.y - startPoint.y);
this.selector.setCameraDimension(width, height);
this.selector.setCoordinates(x, y);
this.camera.repaint(); // important
}
by the way this.selector is SelectorCamera which is the component that paints the selection area.
Step 5
CaptureCamera#endSelection() gets called, this method gets the x,y, width, height from the selection camera and passes it to the capturer class which uses Robot to get screenshot with that rectangle, and before that it removes ALL components from the content pane, and repaints everything and then sets visibility to false.
public void endSelection() {
super.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
int x = this.selector.getCameraX();
int y = this.selector.getCameraY();
int w = this.selector.getCameraWidth();
int h = this.selector.getCameraHeight();
super.getContentPane().removeAll();
super.getContentPane().repaint();
//super.repaint();
super.setVisible(false);
this.c.startCapturing(x, y, w, h);
}
Basically this is the last step, rest steps are unnecessary for the debugging as it only sends back the callback.
I really tried my best explaining the process of my application, I've tried figuring it out for 5 and half hours now, and no luck at all. Tried different ways, by creating new SelectionCamera object as you see, doesn't work.
Why is it doing this? Is it something to do with the swing core?
SelectionCamera code: https://github.com/BenBeri/WiseCapturer/blob/master/src/il/ben/wise/SelectionCamera.java
Thanks in advance.
Based on this example...
try {
final Bootstrap b = new Bootstrap(new ScreenshotCapturer());
b.beginCapture(new ScreenCaptureCallback() {
#Override
public void captureEnded(CapturedImage img) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
b.beginCapture(new ScreenCaptureCallback() {
#Override
public void captureEnded(CapturedImage img) {
System.out.println("...");
JFrame frame = new JFrame();
frame.add(new JLabel(new ImageIcon(img.getBufferedImage())));
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
});
}
});
System.out.println("Hello");
} catch (AWTException exp) {
exp.printStackTrace();
}
I won't focus on the initialise stage of the first round, I will focus on the initialisation of the second round as this is where the problem is...
b.beginCapture call's this.capturer.beginSelection();, which calls super.getCamera().startSelection(); which calls setVisible(true) (CaptureCamera been a JFrame).
This will immediately show what ever was previously displayed on the CaptureCamera. It's important to note here, that no new instances of objects were created through the process...
Now, I made a lot of changes to the base testing this, but it appears that the problem is with the restoration of the frame when it's made visible for the second time. This seems to be an issue with the transparency support of the Window as it seems to restore the last "known" state instead of repainting the window immediately...
Now, I tried clearing the selector before making the CaptureCamera invisible to no eval, as the window seems to be made invisible before the selector is painted.
The final solution I came up with was to call dispose on the CaptureCamera, which releases it's native peer and therefore destroys the old graphics context, forcing the frame to rebuild itself when it is made visible again.
"A" problem with this could be the fact that when all the windows are disposed (and the only running threads are daemon threads), the JVM will exit...
This was an issue during my testing as I was using a javax.swing.Timer to put a delay between the first and second capture process so I could see where the problem was occurring and this caused my JVM to exit (as the timer uses a daemon thread and I had no other windows open).
I got around this by creating a tiny (1x1) transparent window in the Capturer class, keep this in mind if the JVM exists gracefully for no reason ;)
Side Notes...
Now, there is an issue with SelectionCamera (which extends JPanel), it is opaque, but is using a transparent background, this is incredibly dangerous as Swing only knows how to deal with opaque or fully transparent components.
public SelectionCamera() {
super.setBackground(new Color(0, 0, 0, 0));
super.setVisible(false);
}
Should be updated to something like...
public SelectionCamera() {
setOpaque(false);
//super.setBackground(new Color(0, 0, 0, 0));
super.setVisible(false);
}
I'm also confused over the use of super.xxx, the only reason you would do this is if you had overrriden those methods and didn't want to call them at this time...In my testing, I removed all the calls to super where a method wasn't overridden in the current class (and I wasn't already in the overriden method)
Also, the paintComponent method should be calling super.paintComponent
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(0, 0, 0, 0.5f));
g.fillRect(this.x, this.y, this.width, this.height);
}
Make Frame left to be -10,000 then set visible true, add a timer 2 seconds (try lower to 25-100 milliseconds, just to give it sligth pause to invalidate content) , on timer :left to 0 . I think it works due to caching & double buffereing. Frame shows what it had in buffer, buffer points to old image due to caching/ lazy repaint.
Alternative :
Maybe a repaint or invalidate before your show would work too, and don't need to do the left -10,000. I dont work much with ui-swing, just a but years back and remember some strange things like this.
I have tried my best to read around on the topic but I cannot find/understand how to apply the answers to my piece code (and those I have applied don't work)
I have used an example from "Ivor Horton's Beginning Java 2 JDK book" and it is the first time I'm using repaint() but it doesn't seem to work unless I resize the window. It tries to repaint but leaves the screen blank.
Here's the code please let me know if there is something I'm doing wrong.
public class CurveDrawings extends JFrame{
public CurveDrawings (String title){
setTitle(title);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane = new CurvePane(); //Create pane containing curves
Container content = getContentPane(); //Get the content pane
//Add the pane containing the curves to the content pane for the applet
content.add(pane);
MouseHandler handler = new MouseHandler();
pane.addMouseListener(handler);
pane.addMouseMotionListener(handler);
}
//Class defining pane on which to draw
class CurvePane extends JComponent{
public CurvePane(){
quadCurve = new QuadCurve2D.Double(
startQ.x, startQ.y, control.x, control.y, endQ.x, endQ.y
);
cubicCurve = new CubicCurve2D.Double(
startC.x, startC.y, controlStart.x, controlStart.y,
controlEnd.x, controlEnd.y, endC.x, endC.y
);
}
}
class Marker{//not needed for my problem}
class MouseHandler extends MouseInputAdapter{
public void mousePressed(MouseEvent e){
if(ctrlQuad.contains(e.getX(),e.getY())){
selected = ctrlQuad;}
else if(ctrlCubic1.contains(e.getX(),e.getY())){
selected = ctrlCubic1;}
else if(ctrlCubic2.contains(e.getX(),e.getY())){
selected = ctrlCubic2;}
}
public void mouseReleased (MouseEvent e){
selected = null;
}
public void mouseDragged (MouseEvent e){
System.out.println("DRAGGED");
if(selected != null){
//Set the marker to current cursor position
selected.setLocation(e.getX(),e.getY());
pane.validate();
pane.repaint();
}
}
Marker selected = null;
}
public void paint (Graphics g){
Graphics2D g2D = (Graphics2D)g; //Get a 2D device context
//Code to draw each component
}
//Points for quadratic curve
//Points for cubic curve
//More unnecessary code}
Incase it is any help here's the 'Launcher' class for the application
Thanks in advance.
You will need to call
super.paint(g);
in your paint method in your CurveDrawings class to avail of the painting functionality already provided in the JFrame super class.
Note, the standard way of using custom painting in Swing is to use custom components that are based on javax.swing.JComponent that use and override paintComponent. This approach would leverage Swing's optimized painting model.
See: Performing Custom Painting
I have to write a simple Java app which can load pictures, show it in a GUI form, allow the user to apply some transformation, and show the transformed picture.
My solution is working fine, but the UI is flickering a bit, because the repaint method called too often (for example when the user scaling the image with a JSlider)
My code looks like this:
public class ImageCanvas extends Canvas
{
private BufferedImage image;
// ...
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if(image != null)
{
// I draw out the image...
}
}
public void setImage(BufferedImage image)
{
this.image = image;
this.repaint();
}
public void setRotation(double rotation)
{
this.rotation = rotation;
this.repaint();
}
public void setScale(double scaleX, double scaleY)
{
//set the scaling field, then repaint ....
}
// and so on...
}
And, of course, I have an ImageCanvas control on my main UI, and I simply call the public methods (see for example the "setRotation" method above) which repaint the canvas area. I know it's a simple question, but I don't even find a DoubleBuffered property on the Canvas...
Any help appreciated.
Double buffering is built-in for Swing (i.e. JComponent derived) classes.
If you want built-in double-buffering, you should extend JPanel rather than Canvas, and override paintComponent, not paint.
If you can use JPanel than go for it. Please make sure you are not overriding the JPanel.paint method, override JPanel.paintComponent instead.
See this link for details.
Usually graphic lags in these applications can be caused by setting a empty variable at the top of the script, then changing its value, then waiting for the repaint to update it. You could try changing the:
setRotation(double rotation);
so that it rotates the image in that method.
Just a general thing I happen to see while dealing with graphics.
OK so here's my code: http://www.so.pastebin.com/Qca4ERmy
I am trying to use buffers so the applet won't flicker upon redraw() but it seems I am having trouble. The applet still flickers....
Help?
Thank you.
I made a quick video about this problem: http://www.vimeo.com/12035196
Create a Swing applet. Swing is double buffered by default so you should not have this problem. Start with the section from the Swing tutorial on How to Make Applets for the proper way to create a Swing applet.
The best way I've done it is to create another image the same size as your applet, draw to that, then in your paint / update method copy the contents of that image to your graphics object. You have to make sure that you aren't updating the other image when you draw to your applet otherwise it will cause flicker. Drawing should probably be done in another Thread as well, just to make things a little easier to understand.
I don't have access to my code so the following might be a little off (and the code may not be the most efficient):
public class MyApplet extends Applet {
Image offscreen;
boolean pageFlipped = false;
Thread drawingThread;
public void init() {
offscreen = createImage(this.getWidth(), this.getHeight());
drawingThread = new Thread(new DrawingLoop());
drawingThread.start();
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
if (!pageFlipped) {
g.drawImage(offscreen, 0, 0);
pageFlipped = true;
}
}
class DrawingLoop implements Runnable {
public void run() {
while (true) {
Graphics g = offscreen.getGraphics();
if (pageFlipped) {
// do your graphics code here
pageFlipped = false;
}
}
}
}
}
Hope this helps!
-Dan
You can try to solve this issue using a BufferedImage, in this way you just create a BufferedImage that is compatible with your frame and then draw everything there before blitting the whole image onto the JFrame's content.
A better approach is to use automatic buffering with BufferStrategy class, you can read a tutorial about it here.