Drawing images onto AWT and initialising Graphics - java

I'll start off by telling you what Im trying to do if thats OK, as Im not certain the route Im struggling with is even the best way of achieving my ends.
I have a JFrame containing two JPanels. One contains a number of buttons (buttonPanel), the other is, initially, blank (displayPane). When buttons are pressed the stuff shown in displayPanel changes. The way this is working is each press of a button creates a new object that extends JPanel and then adds that to displayPane
So all the above is working fine and dandy (although I freely admit it may not be the best way of doing it) except for one particular case.
In this particular case I need to create a JLayeredPanel and then draw a clipped image on it. JLayeredPanel because I want to draw some stuff on top of it, clipped because I only want to show part of the area (which exact part is passed to the constructor).
Now, the problem Im having is this. The only way I know to draw a clipped image is through g=thingie.getGraphics(), g.setClip(Shape shape), g.drawImage(various). However, all of that relies on being able to get graphics. But because I am assembling the object first there is no graphics object associated with the JLayeredPane (because its not displayed) so getGraphics is returning null and g.setClip() is throwing a Null Pointer Exception.
Obviously I am doing this wrong somehow and somewhere. Any help would be appreciated, sorry if the question is confusing. I tried to include as much detail as possible and now I am a little concerned I've muddied the issue. I'll keep an eye on this and clarify if required.

Warning!: Wrong answer, see below the line
Why don't you just create a new Graphics object, paint on it and then use it with the update() method?
Graphics g = new Graphics();
g.drawStuff();
thingie.update(g);
This showd be correct
As stated on the comments the previous solution was wrong but it can be done with an Double buffer, create a buffered image and draw on it, then override the paint method of the jLayeredPane pane to draw the image.
private void addStuff() {
BufferedImage bi =
new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR);
Graphics bufferedGraphics = bi.getGraphics();
//Paint stuff
bufferedGraphics.drawLine(0, 0, 50, 50);
javax.swing.JLayeredPane layered;
layered = new JLayeredPane() {
#Override
public void paint(Graphics g) {
g.drawImage(bi, 0, 0, null);
}
};
this.add(layered);
this.validate();
this.repaint();
}

Related

JFrame + custom LayoutManeger + work with Graphisc2D (rotation)

I'm working on project, which uses custom Layout Manager. My goal is rotate all components by 180°.
I have some screen which consists of JFrame. I also have some LayoutHandler, which defines some layers of layout. Each layer is instance of LayerLayout which implements LayerManager2 from java.awt
The flow works probably like this:
I have JFrame, which represents the display. This JFrame has some default contentpane. The application set to this contentpane Layout which is represented by LayerHandler:
graphicalDisplayJFrame.getContentPane().setLayout(new LayerLayout(layerHandler));//graphicalDisplayJFrame is JFrame which represents the display
The layerHandler just somehow manage the layers which are LayerLayouts which implemetns LayerLayout2 from java.awt. So it looks like layout of layouts or whatever it is. I'm not much experienced with creating custom layouts.
There I have a situation which shows creating of JFrame.
graphicalDisplayJPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.rotate(Math.PI, anchorx, anchory); //rotation 180°
}
#Override
public void paintChildren(Graphics g) {
super.paintChildren(g);
Graphics2D g2d2 = (Graphics2D) g;
g2d2.rotate(Math.PI, anchorx, anchory);
}
};
graphicalDisplayJFrame = new JFrame(); //creation of JFrame
graphicalDisplayJFrame.setContentPane(graphicalDisplayJPanel); //setting JFrame to apply rotation
The first part should make the rotation 180°, but it does not, even when i call repaint on graphicalDisplayJFrame. BUT when I add this:
JLabel hwLabel = new JLabel("Hello world.");
hwLabel.setVisible(true);
graphicalDisplayJFrame.add(hwLabel);
I cause strange thing. The JFrame and all of the components on it is accidentaly rotated by 180°. But its only static behaviour, because there are some text components on display and when there is changed the text on them its again rendered in non rotated way.
So the question is why this could happens?
If i divide it in two parts:
a) Why the Graphics2D on JFrame does not affect the children components on its content pane. Why it only works when I add the JLabel which has no sense here but it was just experiment which caused what I wanted (= repaint with right rotation).
b) I should probably somehow apply the rotation => something like overriding the paint method on each components which are added on the contentPane of JFrame. But how should can I do it? Becouse upper described layout handler just manage a bunch of layouts (layers which are probably custom layouts implementig LayoutManager2 from java.awt) and it is just feeding the components which are put on the layouts and put them on content pane. So the question should probably be where could be the place where I want to implement my custom paining which means apply the rotation (which is this part of code g2d.rotate(Math.PI, anchorx, anchory); //rotation 180°).
I hope it understandable, I made pretty bit effort to understand where could be problem and trying find out the solution, but its behave really strange. So I hope someone with more experience with this could help me out off this struggling and show me the way.
You are applying the rotation at very strange points. Graphics2D.rotate() applies only to things painted after the rotate call, but your paint-overides rotate after calling super.
As for why adding a label causes rotation, hard to tell without seeing the entire code. Probably an effect of partial repaint.
You should not modify essentials like the graphics translation or rotation of the graphics passed to your paint methods. It has the potential to affect the parent components drawing in unexpected way and the effect can easily change depending on JRE version. Instead create a new graphics to pass down to your child components:
#Override
public void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
try {
g2d.rotate(Math.PI, anchorx, anchory);
super.paintChildren(g2d);
} finally {
g2d.dispose();
}
}
This method leaves the original Graphics untouched, at least allowing the parent component to render normally. Rotating the graphics is still tricky and will probably have unintended side effects in the rendering process, but at least these effects are now carefully limited to the children of the rotating component.

drawing to a JPanel without inheritance

Right now I'm working on a program that throws up a bunch of separate (generated at runtime) images, each in their own window. To do this i've tried this approach:
public void display(){
JFrame window = new JFrame("NetPart");
JPanel canvas = new JPanel();
window.getContentPane().add(canvas);
Graphics g = canvas.getGraphics();
Dimension d = getSize();
System.out.println(d);
draw(g,new Point(d.minX*50,d.maxY*50), 50);
window.setSize(d.size(50));
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
window.setVisible(true);
}
public void draw(Graphics g, Point startLoc, int scale){
// generate and draw the image
}
public Dimension getSize(){
//returns my own dimensions class
}
However, this throws a NullPointerException in draw, claiming that the graphics is null. is there any way to externally draw to a JPanel from outside it (not inherit from JPanel and override PaintComponent)? Any help would be appreciated.
If you are drawing your images at runtime you should use BufferedImage.
Create a BufferedImage, call getGraphics() on it to get its Graphics object, draw to it using the Graphics(2D) api, then call Graphics.dispose() (not strictly necessary but a good habit) and as the previous poster suggested either create an ImageIcon with it and put in a JLabel using setIcon() or subclass JPanel / JComponent and draw it in paintComponent
You're finding out that getGraphics() won't work in this way since the Graphics object obtained is null prior to the component being rendered, and even when it is not null, it is not stable and becomes invalid with each redraw. Possible options include:
Creating ImageIcon from each Image, placing the icon in a JLabel and displaying the JLabel.
Biting the bullet and extending JPanel or other JComponent (you don't say why you're trying to avoid this) and displaying the image in the JPanel's paintComponent method.
Doing the above, but first creating a BufferedImage which is displayed in the paintComponent(...) method. Then you can modify the BufferedImage during the program's run if you need to change the display, add new images,...
a program that throws up a bunch of separate (generated at runtime) images,
each in their own window
don't do it that this way, don't create a lots of JFrames, these Object stays in the memory, until current JVM instance exists, result from this concept pretty could be OutOfMemory exceptions
don't create lots of JFrames, create only one JFrame, rest of Containers could be JDialog or JWindow
don't create a new JFrames, JDialogs or JWindows on the runtime, re_use the existing Containers
put these images as Icons to the JList or maybe better would be look at CardLayout

creating a pen tool, repainting issue - JAVA

I am trying to create a pen tool using mouse listeners:
public void mouseDragged(MouseEvent e) {
imageL.setCoordinates(originalPos, e.getPoint());
imageL.repaint();
originalPos = e.getPoint();
}
The paint function in the JLabel (imageL) receives two sets of points that allow drawing a line based upon the mouse drag. The only issue is that every time the drag is performed, the new layer does not contain the line drawn from the previous mouse drag. The paint function of the JLabel is as follows:
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(drawingColour);
g2d.drawLine(originCors.x,originCors.y,endCors.x,endCors.y);
}
So essentially my question is: How do I "add" the new line to the current layer?
any help would be great,
Thanks in advance
You want to draw in a JComponent's (and JLabel extends from JComponent) paintComponent method, not the paint method unless you plan to handle all the painting of the component's borders and children. Next, you may wish to have your drawing component hold an ArrayList of Point and add Points to this array list in the mouseDragged method. Then the paintComponent method can iterate through the list and paint all the lines. If all you want to do is paint one line, then have the painting JLabel hold both points in a class field.
Michael,
For a start, in swing (a JLabel is a swing component, as apposed to the older AWT library) the recommended practice is to override the paintComponent method (not the "paint" method). See The Java Tutorials: Custom Painting for the hows & whys of it.
If you want to custom-paint a-list-of-lines then you're going to have to do just that... not just the "new" one. One way around this is to "update" an image with each new line, and then custom-paint that... this is slightly quicker as you only "paint" the each line once, at the cost of using more memory (to maintain the "backgroun image")... but this technique lends itself to "double buffering", which avoids "that flickering" you get when you draw directly to the screen. See The Java Tutorials: Working with Images for the details. It's actually pretty straight forward.
I suggest you google for "java double buffering example" (avoid Rose India, it's full of crap).
Cheers. Keith.

Add image over another image?

Im trying to make a turn-based game, progressing OK so far, however now I came to the part where I want to be able to move units around. I use images to determine different types of terrain, and another image to display a unit, but I can't get the unit to come out OVER the terrain, it only pops up under the terrain. Seems like I'm the first one ever to bump into this problem, so am I going at it the wrong way?
The tile with a terrain-image:
protected void paintComponent(Graphics paintIt) //paints this sexy-hex
{
super.paintComponent(paintIt); //not sure if we need this? lol
paintIt.setColor(is); //sets the color (later not even used, we need pics and shit)
//paintIt.fillPolygon(hex); //fills the hexagon with the desired color, will not be used later on either
if(isExplored)
paintIt.drawImage(image, 0, 0, null);
else
paintIt.fillPolygon(hex); // I want to paint it black
if (hoover) //are we pointing at this cell?
{
paintIt.setColor(Color.WHITE); //we want the nice line around the cell to be white
Graphics2D g2 = (Graphics2D) paintIt;
g2.setStroke(new BasicStroke(3)); //and 3 pixels wide (we are allowed to change it, supposedly in proportion to "rad"
g2.drawPolygon(hex); //draws the line around the hexagon
}
}
The unit:
protected void paintComponent(Graphics paintIt)
{
if(!selected)
{
paintIt.drawImage(image, 0, 0, null);
paintIt.drawImage(image2,0,0,null);
/*
super.paintComponent(paintIt);
paintIt.setColor(Color.BLACK);
paintIt.drawPolygon(shape); */
}
else
{
super.paintComponent(paintIt);
paintIt.setColor(Color.RED);
paintIt.drawPolygon(shape);
}
}
Sorry for the comments, and sorry for some code that's not even used, just think it's best to have it until the project's finished if we need to get back to somewhere.
The unit image is smaller than the terrain, so it fits. I've tested without painting out the terrain so I know the unit is painted under it.
If there's still code you would need to get an idea what I'm doing, tell me, just thought this was the relevant parts.
EDIT #2
The tile is a class created from another class which extends JFrame. The unit-class is created from that JFrame class but is beeing stored in the tile. Both tile and unit are JComponents.
Fix'd, I had to draw the units image within the terrain-class to make it come out on top. Since I already stored the unit in the class I don't know why I didn't do it this way from the beginning.
Thanks anyhoo <3

paintComponent() leaving behind previously drawn images after repaint()

I have a JPanel which draws .png images. each image has 2 copies to signify whether or not it has been selected. One image is normal and one has a colored border to signify the selection has been made. (You can think of these images as simple shapes for the sake of argument)
Which version of the image is drawn is determined via MouseListeners. If the user selects image1, then image1 will display its highlighted version.
In general this is working, however upon repaint() the highlighted version of the image is left behind. Since the highlighted version is slightly larger than the normal version, you can see it sticking out behind the newly drawn normal image (overlapping).
I thought repaint() was supposed to completely dispose of the current drawings and start from scratch, this doesn't seem to be the case here. Below is the general idea, blackClicked is toggled inside a MouseListener.
So my question is how do I make repaint() get rid of its currently drawn images and start from scratch.
Also, if there is a simpler way to achieve all of this please let me know, making 2 copies of images is tedious work.
#Override
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
if(blackClicked)
g2.drawImage(blackSelected,0, 0, null);
else
g2.drawImage(black,0, 0, null);
g2.drawImage(green,0, 0, null);
}
how do I make repaint() get rid of its currently drawn images and start from scratch.
You mean, how to make repaint fill the component with it's background color before painting? How about adding
g2.setColor(getBackground());
g2.fillRect(0, 0, getWidth(), getHeight());
in top of paintComponent()?

Categories