does java bufferedImage really give a black canvas? - java

I was trying to achieve the same thing with two different approaches. I want to draw a simple rectangle.
In the first approach, I simple get g object and draw directly on it.
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(100, 100, 50, 50);
}
In the second approach, i get a image canvas draw on it and then draw the image on the graphics object of jpanel.
public void paintComponent(Graphics g){
super.paintComponent(g);
BufferedImage img = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics a = img.getGraphics();
a.drawRect(100, 100, 50, 50);
g.drawImage(img, 0, 0, 500, 500, null);
}
I was hoping both would result in similar output but the one below where i am drawing an image gives white rectangle on black background while the one on the top gives black rectangle on a white background. Why is this so?

When you do custom painting on a component. The background is painted first. It appears you are extending JComponent which will simply paint a white background. Other Swing components will use the color specified by the setBackground() method, for example a JPanel defaults to the grey color. The graphics object is then set to be the foreground color of the component so you can do your custom painting. The default foreground color is black.
When you create a BufferedImage it looks like it is filled in with a black background by default and then the color of the Graphics object is set to WHITE so you can do custom painting and it will show up on the black background. You can always fill the background of the buffered image to be any color and then change the graphics object to use any color for the foreground.

You don't call setColor(), so the rectangle is drawn with the default colour. The default colours are different in the two examples, which is why you should always set the colour explicitly (and fill the background with your background colour).

You are using TYPE_INT_RGB. You can explore the other types at this link. TYPE_INT_ARGB supports a transparent background, which is what you may be looking for.
But it is not as simple as setting the type. The background color is also affected by the component type when you embed it, or by the file format when you save it as a file. PNG, for example, is a file format that supports transparency. You will have to experiment with different component types to see whether they affect transparency or not.

Related

Performance-minded way to draw a scaled image in a JPanel

I have a Java Application that includes functionality to load in photos to show a user that they choose, but I want the width of each photo to be a max of 200 pixels.
My current solution is to use a class that subclasses JPanel, and in its constructor, I load the image and calculate the correct size.
I override paintComponent(Graphics g) and draw the image there using:
super.paintComponent(g);
g.drawImage(image.getScaledInstance(width, height, Image.SCALE_FAST), 0, 0, this);
However, this resizes the image once every frame, since paintComponent is called every frame, and I’d prefer to only have to resize the image once and draw that.
Is there a way to achieve better performance, doing what I need it to do?

Stop JScrollPane from redraw JPanel

I have a JPanel with a JScrollPane sorounding it, the problem is that when i use the JScrollPane the JPanels redraw methode get called. I want to disable that because my JPanel redraw by its self at the right time.
I want it so it just updateds the getClipBounds() for the paint methode but withoud calling the paint methode.
You can't do that - since the viewport displays different parts of the contained JPanel, depending on the position of the scrollbar, the areas that have to be repainted might in fact be newly revealed and might not have been painted before.
Since JScrollPane doesn't know how the contained Component is implemented and whether it repaints its entire area or only the area that needs repainting, it forces the contained Component to redraw itself upon scrolling.
However, you can instead render the content you want to show to a bitmap, and then paint the bitmap in the paintComponent(Graphics) method. Thus, you effectively buffer your painted content and can initiate an update to the buffered bitmap whenever it suits you.
In order to paint onto a bitmap, you can do this:
BufferedImage buffer; // this is an instance variable
private void updateBuffer(){
// Assuming this happens in a subclass of JPanel, where you can access
// getWidth() and getHeight()
buffer=new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g=buffer.getGraphics();
// Draw into the graphic context g...
g.dispose();
}
Then, in your JPanel, you override the paintComponent method:
public void paintComponent(Graphics g){
g.drawImage(buffer, 0, 0, this);
}

Image only paints within original bounds

I want to animate several jpgs in a JFrame, I'll show you some extracts:
My class constructor that extends JFrame
super(title);
setLayout(null);
setResizable(false);
setSize(Settings.windowWidth, Settings.windowHeight);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
My class constructor that extends JPanel
i = new ImageIcon(image).getImage();
setSize(i.getWidth(this),i.getHeight(this));
setBounds(x, y, i.getWidth(this), i.getHeight(this));
The overwritten method
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(i, getX(), getY(), null);
}
Yes, I know null layout isn't preferrable, but unless you have a better idea for absolute positioning I'll stick with it for now.
Above code does paint the image, starting at (x,y), but not completely.
For 50 and 100 it shows this:
Which is pretty much: It only paints the image within a 256x256 box (image dimensions) from 0,0, no matter where it has been relocated to.
Any advice, help, solutions, suggestions?
If you need more code, ask me, just don't feel like putting everything around it in here, too ;)
There is no need for custom painting:
Add the ImageIcon to a JLabel and add the JLabel to a JPanel
Change the location of the label on the panel when you want to animate it.
Or, if you do custom painting then there is no need for a null layout.
You override the getPreferredSize() method of the JPanel and add your panel to the frame.
Then in the paintComponent() method you can paint the image where every you want withing the bounds of the preferred size that you set.
My gut feeling is you don't understand how component painting actually works...
First, you do this...
i = new ImageIcon(image).getImage();
setSize(i.getWidth(this),i.getHeight(this));
setBounds(x, y, i.getWidth(this), i.getHeight(this));
Then you do this...
g.drawImage(i, getX(), getY(), null);
which seems to be painting the image at a offset position from the components origin, but since the component is sized to match the size of the image, the image is cropped at the component boundaries.
When a component is painted, the Graphics context's origin is set to the components location, meaning that 0x0 is now the components top/left corner.
You can test this by using setBorder(new LineBorder(Color.RED)), which will show you the physical bounds of the component
In your case, something like
g.drawImage(i, 0, 0, this);
In your case, you should be moving the component not the image.
Personally, I'd add the JPanel to the JFrame using a BorderLayout, then you can simply move the image anywhere within the context of the component itself. Remember to override getPreferredSize to return an appropriate size for your purposes so the frame can be packed around it more effectively.
It's tricky to do animation with components (not impossible, there's just a lot to take into account), generally it's just easier to paint directly to a canvas like a JPanel, but that's me
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works

How to draw an image over JPanels

I have a Battleship game in which I am trying to draw the ships, hits, and misses on the Grid object. Grid is an instance of JPanel with many Blocks. Blocks are JPanels also but are attributes of the Grid. The ships are being drawn on the Grid but under the Blocks. Is it possible to draw over the Blocks? I tried the Glass Pane and it didn't work too well. Any other solutions?
Is it possible to draw over the Blocks? I tried the Glass Pane and it
didn't work too well. Any other solutions?
Yes, a non-recommended but sometime useful approach is to use an extended JPanel and override the paint(Graphics g) function:
Class MyGridPane extends JPanel
{
#Override
public void paint(Graphics g) {
super.paint(g); // <----- don't forget to call this
// then anything you draw will appear above the child component's
Graphics2D g2d = (Graphics2D)g.create(); // cloning to work, it is safer aproach
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();// disposing the graphics object
}
}
Alternatively you may look into using JLayeredPane to work with a Panel containing your drawn image above the target panel(Grid)'s layer.
Check out How to Use Layered Pane
Blocks are JPanels also but are attributes of the Grid. The ships are
being drawn on the Grid but under the Blocks.
There's a sneaky way to achieve it, but you may need to change your base layout to GridBagLayout.
Basically, you want to add each Block to its own cell but GridBagLayout will allow you to add components that can expand columns and/or rows, allowing to add your ships that expand over Blocks.
This, of course, assumes you ships and effects are based on components

Java2d: JPanel set background color not working

I have the code shown below:
public VizCanvas(){
{
this.setBackground(Color.black);
this.setSize(400,400);
}
}
It worked fine and displays the panel in black background. But when I implement the paint method, which does nothing, the color changes to default color i.e gray.
I tried to set graphics.setColor() but it didn't help.
You need to do a fill of the canvas to your background colour in the painting method. Something along the lines of:
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
After that, draw whatever you need to. You could also try calling super.paint(g) in the paint method instead before doing anything.
Custom painting should be done by overriding the paintComponent() method, NOT the paint() method. Then all you do is invoke super.paintComponent() to get the background painted.
Setting the size of the component does nothing. The layout manager will override the size. You should be setting the preferred size or override the getPreferredSize() method.
Read the Swing tutorial for Swing basics. There are sections on "custom painting" and "using layout managers".

Categories

Resources