Java buffer strategy sometimes doesn't draw properly - java

I created a game in Java which uses buffer strategy. But sometimes I just get a white frame or a white border. That happens about every fifth time. I searched a lot but I really can't figure out what I'm doing wrong. The code compiles and there aren't any errors printed out.
If if fails, the frame looks for example like that:
white border
completely white frame
Here's code (only the relevant parts):
private BufferStrategy bs;
public Testclass(){
setResizable(false);
setSize(1000,600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setIgnoreRepaint(true);
setVisible(true);
createBufferStrategy(2);
bs = getBufferStrategy();
}
protected void paint() {
do {
Graphics2D g=null;
try {
g = (Graphics2D) bs.getDrawGraphics();
g.setColor(Color.CYAN);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
} finally {
g.dispose();
}
bs.show();
} while (bs.contentsLost());
}
public static void main(String[] args) {
Testclass window = new Testclass();
window.paint();
}

I actually found a better way try this:
boolean BufferStrategyRunning = true;
while(BufferStrategyRunning == true) {
BufferStrategy BS = GUIFrame.getBufferStrategy();
if(BS == null) {
GUIFrame.createBufferStrategy(3);
continue;
}
Graphics g = BS.getDrawGraphics();
g.drawImage(CurrentScreen.BackgroundImage, 0, 0, null);
g.dispose();
BS.show();
}

Related

How to draw transparent image in java with BufferedImage?

I'm willing to show an transparent image on a canvas in java with the help of graphics2D and BufferedImage.
Here is the code which loads image.
private static BufferedImage sprites,board;
public static void load(){
try {
board = new BufferedImage(100,100,BufferedImage.TYPE_INT_ARGB);
board = ImageIO.read(new File("res/chesssprite.png"));
} catch (IOException ex) {
Logger.getLogger(SpriteManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
and here is the code which renders the image
public void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
Graphics2D g2d = (Graphics2D) g;
{
g2d.setColor(new Color(150,150,150));
g2d.fillRect(0,0,getWidth(), getHeight());
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(board,0,0,null);
}
g = g2d;
g.dispose();
bs.show();
}
I have search on net a lot but didn't come with an solution. If anyone knowns how to fix this.
Here is the image..
And here is how output looks like
Okay whoever facing these kind of problem:
Make sure the image is transparent. Test it in image viewer.
Remove the line g2d.setComposite(AlphaComposite.Src); This line adds alpha composites which make every transparent pixel black.

Is setting the BufferStrategy every time the game renders a good idea?

For some reason everyone sets their BufferStrategy to the BufferStrategy in their canvas whenever they render their game.
BufferStrategy bs;
Graphics2D g;
private void render() {
bs = window.getCanvas().getBufferStrategy();
if (bs == null) {
window.getCanvas().createBufferStrategy(3);
return;
}
g = (Graphics2D) bs.getDrawGraphics();
bs.show();
g.dispose();
}
// When thread starts
public void run() {
while (running) {
//Game loop stuff here.
render();
}
}
Can you just set it once instead of every time the game re-renders?

Java Passing 2D Graphic as a Parameter

I have a function that is drawing an image and then saving it immediately after but the problem is that it seems to be drawing it twice, once for the view on the screen and then once to save it to the disk
public class myFrame {
public static void main(String[] args) {
JFrame lv_frame = new JFrame();
// setup jframe here
lv_frame.add(new image());
lv_frame.setVisible(true);
}
}
class image extends JPanel {
public void paintComponent(Graphics graphic) {
super.paintComponent(graphic);
draw(graphic);
save();
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.fillArc(0, 0, 250, 250, 0, 90);
}
public void save() {
BufferedImage image = new BufferedImage(250, 250, BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D graphic2D = image.createGraphics();
try {
File output = new File("file.png");
// is it possible to use the graphic I've already
// drawn here instead of re-drawing?
draw(graphic2D);
ImageIO.write(image, "png", output);
} catch(IOException log) {
System.out.println(log);
}
}
}
I have a function draw which creates the image on the gui screen and another function save which saves it to the disk but the save function is calling draw as well.
Is it possible to save the image that has already been drawn without re-calling the draw function as it is being done in my code?
I was answering your question on Display to GUI and Save to Disk with a Single Object/Variable, however it was closed probably due to the similar nature of your question.
I think there are several issues which you seemed confused with and I would like to write my solution here which also addresses your question in your duplicated post.
Is it possible to save the image that has already been drawn without re-calling the draw function as it is being done in my code?
Don't be confused between drawing an image on the Panel and saving it. The following shows a working example on saving a drawn image.
class DrawingSpace extends JPanel
{
private BufferedImage buf;
public DrawingSpace(){
setPreferredSize(new Dimension(200, 200));
buf = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
drawOnBuffer();
}
public void drawOnBuffer(){
Graphics g = buf.createGraphics();
g.setColor(Color.BLUE);
g.fillOval(0,0,200,200);
g.setColor(Color.RED);
g.fillOval(50,50,100,100);
g.dispose();
}
public void saveBufferAsImage(String pathname){
String fmt = "";
if(!(pathname == null || pathname.equals(""))){
fmt = pathname.substring(pathname.lastIndexOf(".")+1);
File outputfile = new File(pathname);
try{
ImageIO.write(buf, fmt, outputfile);
}catch(IOException ioe){System.out.println("Unable to save file");}
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(buf, 0, 0, 200, 200, null); //Only for user to see
}
}
To save an image, you not necessarily have to draw and display it in the JPanel first. Note that I created a BufferedImage called buf and I am drawing on buf. Once I have drawn onto a BufferedImage, I can simply save that image as a file (I don't have to draw it to the panel first).
Notice that I did the following:
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(buf, 0, 0, 200, 200, null); //Only for user to see
}
g.drawImage(buf, 0, 0, 200, 200, null) will draw whatever on buf onto the JPanel. It can be deleted and the saving will still work. When I draw it on the panel, it is only for the user to see the outcome of the drawing.
To test the program:
class SaveImageRunner{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
JFrame frame = new JFrame("Saving a buffered image");
DrawingSpace ds = new DrawingSpace();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(ds);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ds.saveBufferAsImage("BlueCircle.png");
}
});
}
}
Saved Image:
Some pointers on drawing:
paintComponent(Graphics) shall only contain codes for drawing and nothing else. Do not implement your codes for saving the image to a file inside here.
Try not to create a new BufferedImage in the paintComponent(Graphics). If not, a new instance of a BufferedImage will be created on every repaint.
Change the place where you create the graphics2D object and reuse it.
Try this out.
public class myFrame {
public static void main(String[] args) {
JFrame lv_frame = new JFrame();
// setup jframe here
lv_frame.add(new image());
lv_frame.setVisible(true);
}
}
class image extends JPanel {
public void paintComponent(Graphics graphic) {
super.paintComponent(graphic);
// Image and graphics are now created here
BufferedImage image = new BufferedImage(250, 250, BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D graphic2D = image.createGraphics();
draw(graphic2D);
save(image);
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.fillArc(0, 0, 250, 250, 0, 90);
}
public void save(BufferedImage image) {
try {
File output = new File("file.png");
ImageIO.write(image, "png", output);
} catch(IOException log) {
System.out.println(log);
}
}
}
EDIT
I have found the answer in another post. It is not a bad idea to make the drawing twice. What you are doing is, like #Hovercraft says in this post, separating the screen drawing from the writing to files so that you don't see your graphics drawing performance hurt.
I have tried to make the drawing only once but you have no easy method to store the drawing Graphics object. Probably, it is implemented to prevent this. If you see the way the method works, you are given the Graphics object with the only purpose to draw on it. Using it in other ways could impact the performance.

BufferedImage black and white after scaling

After scaling BufferdImages of nation flags about 50% of all flags are displayed in black and white (not grayscale) and few do look weird, like turky:
Here's a black and white example
Here I set the flag
protected void updateFlag(BufferedImage flag){
int height = pnlFlag.getHeight();
int width = (int)(1f * flag.getWidth() / flag.getHeight() * height);
BufferedImage scaledFlag = new BufferedImage(width, height, flag.getType());
Graphics2D g2d = scaledFlag.createGraphics();
g2d.drawImage(flag, 0, 0, scaledFlag.getWidth(), scaledFlag.getHeight(), null);
g2d.dispose();
pnlFlag.flag = scaledFlag;
pnlFlag.repaint();
}
And my JPanel
class FlagPanel extends JPanel{
private BufferedImage flag;
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if(flag == null) return;
g.drawImage(flag, 0, 0, null);
}
}
I'm pretty sure you can not just change the height and width of a Buffered Image and it will scale for you.
Try using getScaledInstance() like in this question.
The code bellow works fine, but you need to call the repaint method because the getScaledInstance method take a time to generate the scaloned image.
public class Main extends JFrame {
private ImageIcon ico;
private boolean resize = true;
private Image scale;
public Main() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
ico = new ImageIcon("image.jpg");
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D) g;
if (!resize) {
g.drawImage(ico.getImage(), 0, 0, null);
} else {
if (scale == null) {
scale = ico.getImage().getScaledInstance(ico.getIconWidth() / 2, ico.getIconHeight() / 2, Image.SCALE_SMOOTH);
}
g.drawImage(scale, 0, 0, null);
// gg.scale(0.5, 0.5);
// gg.drawImage(ico.getImage(), 0, 0, null);
}
}
public static void main(String[] args) {
Main m = new Main();
m.setSize(600, 600);
m.setVisible(true);
}
}

I cant view a BufferedImage image on a JPanel

I am having a problem with the code below. If I load the image directly into the JPanel I can see it. But when I try to draw it first to the BufferedImage before drawing the BufferedImage on the JPanel the image is not visible. What am I doing wrong?
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.JPanel;
/**
*
* #author Duafeb
*/
public class RTester {
BufferedImage backBuffer;
Graphics2D g2;
Pane pain;
Image img;
public RTester(){
JFrame frame=new JFrame("Sprite Tester");
frame.setSize(1200, 700);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
backBuffer= new BufferedImage(1200,700,BufferedImage.TYPE_INT_RGB);
g2=backBuffer.createGraphics();
pain=new Pane();
frame.add(pain);
Toolkit tk=Toolkit.getDefaultToolkit();
img=tk.getImage(this.getClass().getResource("running.png"));
frame.setVisible(true);
}
public class Pane extends JPanel{
#Override
public void paintComponent(Graphics g){
Graphics2D g3=(Graphics2D)g;
g3.drawImage(backBuffer, 0, 0, this);
}
}
public void display(){
g2.setColor(Color.yellow);
g2.fillRect(0, 0, pain.getWidth(), pain.getHeight());
g2.drawImage(img, 0, 0, pain);
pain.repaint();
}
public static void main(String[] args){
RTester test=new RTester();
test.display();
}
}
There are a few things that don't feel right about this...
The first is, you create a Graphics context to BufferedImage, but never dispose of it. Be careful, on some systems this can prevent the contents from been rendered, but this might relate to the screen device rather than a BufferedImage
For example, if I alter you code to paint the contents directly within the paintComponent method instead of to the BufferedImage, the image will be displayed (all bit a split second after the window becomes visible).
I'm not sure what it is you're trying to achieve by using the BufferedImage, but you could achieve the same thing straight through the paintComponent method
Instead of using Toolkit.getImage, you could use ImageIO.read, which guarantees that when it returns, the image is fully loaded (or will throw an IOException if it fails) or as #Reimeus had previously suggested, using a MediaTracker to ensure that the image is properly loaded before you continue using it.
So, you have four options....
One
Use a MediaTracker to wait for the image to be loaded...
MediaTracker mt = new MediaTracker(frame);
mt.addImage(img, 1);
try {
mt.waitForAll();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
Two
Use ImageIO.read instead...
img = ImageIO.read(this.getClass().getResource("running.png"));
Three
Render output directly in the paintComponent method...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g3 = (Graphics2D) g;
g3.setColor(Color.yellow);
g3.fillRect(0, 0, pain.getWidth(), pain.getHeight());
g3.drawImage(img, 0, 0, obsever);
}
Four
Use you own ImageObserver to ensure that when the image is updated, you re-render it to the backing buffer...
private MyImageObsever obsever;
public void display() {
if (obsever == null) {
obsever = new MyImageObsever(this);
}
g2.setColor(Color.yellow);
g2.fillRect(0, 0, pain.getWidth(), pain.getHeight());
g2.drawImage(img, 0, 0, obsever);
pain.repaint();
}
public class MyImageObsever implements ImageObserver {
private RTester tester;
public MyImageObsever(RTester tester) {
this.tester = tester;
}
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
tester.display();
return (infoflags & (ALLBITS|ABORT)) == 0;
}
}

Categories

Resources