I want to do efficient 2D drawing in Java. I would like to have some kind of surface which I may draw freely without having to let the view hierarchy traverse and update, which may cause stutter.
I have used a JPanel at first and called repaint() but I find it not to be optimal (and it is the reason I ask). The closest thing I have worked with is the Android's SurfaceView and it gives me a dedicated drawing surface.
To achieve this dedicated drawing surface, do I need to use OpenGL or is there any equivalent SurfaceView?
If you don't need Accelerated Graphics, you can draw onto a BufferedImage with Graphics2D. Once you have your data in the BufferedImage, you can simply paint the BufferedImage onto the component. This will avoid any sort of flickering you are talking about.
Creating the BufferedImage is easy:
int w = 800;
int h = 600;
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Then you can draw objects onto it with a graphics context (Possibly in your own render function):
Graphics2D g = bi.createGraphics();
g.drawImage(img, 0, 0, null);
//img could be your sprites, or whatever you'd like
g.dipose();//Courtesy of MadProgrammer
//You own this graphics context, therefore you should dispose of it.
Then when you repaint your component, you draw the BufferedImage onto it as one piece:
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(bi, 0, 0, null);
}
Its sort of like using BufferedImage as a back buffer, and then once you are done drawing to it, you repaint it onto the component.
Related
I'm making a game where you start by drawing your map. At the moment it works by having an Area instance variable and then when the player clicks/drags the mouse it adds Ellipse2Ds to it. Here's what I mean:
Area land = new Area();
And then in the MouseDragged method
Point2D mouse = e.getPoint();
Ellipse2D ter = new Ellipse2D.Double(mouse.getX() - drawRad, mouse.getY() - drawRad, drawRad*2, drawRad*2);
land.add(new Area(ter));
And then basically the same except land.subtract(new Area(ter)) for erasing.
My problem with this is that after doing a lot of drawing it becomes very slow to draw the Area. the other problem is that I would like to be able to easily get the outline of the drawing, and I haven't found a good way of doing that using Areas. Using area.getPathIterator is not nearly accurate enough.
So I'm wondering what other ways of saving drawings are? I can't just have an array of Ellipses because then erasing wouldn't work.
Thanks!
If you are drawing the same thing over and over it may be worth while to draw it to an image once or load it from a file at start up and then just paint the image rather than painting all the individual shapes.
To load from a file, put your picture file in the same directory as your .java file and load:
BufferedImage img = ImageIO.read(this.getClass().getResource("picture.png"));
To draw on the image once:
BufferedImage img = new BufferedImage(width,height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = img.createGraphics();
In your paintComponent method you draw the image somewhere.
g.drawImage(img, 0, 0, this);
If you have something moving over a background, instead of adding and subtracting the moving item. Just draw the background and then draw the thing that moves, drawing the background will effectively erase the old position with less computation.
I recently watched a YouTube video of Notch coding the game "Minicraft" from scratch for the Lundum Dare competition. The game is in Java, and what confused me is that he has a BufferedImage object that he modifies and draws to the screen like this:
private BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
then
g.drawImage(img, extra parameters of the coordinates, etc);
When I was learning how to use the Java Graphics object, I learned to separately draw all the things that I wanted on my screen like this:
g.drawImage(player, ...);
g.drawImage(background, ...);
etc...
But it seems he builds the BufferedImage based on what each of the individual parts look like (player, background, ...) and then just draws that only. What is the value in doing this? What is the difference between drawing one image instead of individually drawing all the images?
I am playing around with graphics in Java using JFrames and am trying to completely understand when Graphics changes actually occur.
Let's say I have a timer running every second, and every second the graphics change. The tick method, for example, will run every second...
public void tick(Graphics g) {
g.fillRect(0,0,10,10);
//Do Some Computations
g.fillRect(0,0,someComputedWidth,someComputedHeight);
}
Are the graphics instantly updated on the computers display? When the first fillRect method is called, is that rectangle instantly created?
I want to be able to draw all my graphics, and then make them have their effect at the same exact time. I want the tick method to buffer all updates, and then execute them all at once. How do I accomplish this?
To buffer your updates you can use BufferedImage:
#Override
public void paintComponent(Graphics g){
BufferedImage image = new BufferedImage(this.size.width, this.size.height, BufferedImage.TYPE_INT_RGB);
Graphics bgr = image.getGraphics();
for(Tile t : this.tiles){
t.draw(bgr);
}
super(bgr);
g.drawImage(image, 0, 0, null);
bgr.dispose();
image = null;
}
I used this method in one of my school projects. It draws to an off-screen image and then draws the image to display.
With few drawable objects being drawn, and no, or little animation, updating directly to the display does not always have noticable effects, however with more complex tasks the image "flickers" and double buffering is a perfect cure for that.
I know Im not the first person to ask this type of question, but I think mines a little bit different.
I have a png image I drew on MS paint that is a player and I want the background of the image to be transparent when I use a graphics objects to draw the image. I tried some stuff with magic pink but It doesn't seem to be working the same in java. Im not new to java, but Im inexperienced so could you explain any packages or methods that you use thanks!
You will need to use AlphaComposite to have the transparency effect:
Assuming that you already know Graphics2D and Graphics uses BufferedImage
Creating temporary graphics object g.create() and then dispose the object for safely restore the state of graphics object changed after the object creation.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.drawImage(tileImage, 0, 0, getWidth(), getHeight());
g2d.dispose();
// draw player image
}
With Java6, a PNG picture should be used for TrayIcon, but as mentioned in this SO question, check:
the background color chosen to represent the transparent pixels
the transparency options
the resolution of the icon
alternate format like SVG (provided you are using external library like Batik, and
conversion mechnism to java.awt.Image)
is there a possibility to draw a JPanel on a specific location op a Graphics (or Graphics2D) object?
I override the paint method of my canvas, and call panel.paint(g) in there, but it does not work the way I woul hope.
#Override
public void paint(Graphics g){
Dimension size = panel.getPreferredSize();
panel.setBounds(pos.x, pos.y, size.width, size.height);
panel.paint(g);
}
the size object is correctly defined as I would wish, so that's not the problem. Also, the pos contains a correct x and y on the screen.
You should probably be using paintComponent instead of paint, since the latter is the AWT method, and the former is the Swing method.
One nice thing about Swing's paintComponent is that the Graphics passed is actually always going to be a Graphics2D, so you can:
Graphics2D g = (Graphics2D)lg;
Now you can use the getTransform to save the old transform, then modify the transform of the Graphics2D using either setTranform or the scale, translate and rotate methods. Don't forget to restore the old transform, or you'll likely fudge the next thing being drawn by that context.
I'll throw in that, depending on the circumstance, drawing to a BufferedImage might be appropriate. You can get a Graphics context using BufferedImage.getGraphics(). Then you can draw the BufferedImage's context by whatever means suit you.