0

So, I am trying to do a paint program, I have added some features, but now I want to be able to save the picture. I open the program, draw a picture, save it and everything works! Except for the fact that the image is now completely black. Thank you in advance!

(Please tell me if I have bad code somewhere since I am still a learning Java programmer and it would greatly benefit me in the future)

Classes:

Main class:

import javax.swing.JFrame;



public class Test{
public static void main(String args[]){

    Ploofer ploof = new Ploofer();
    ploof.setSize(1000, 950);
    PumpkinPie f = new PumpkinPie(ploof);

    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(1000,1000);
    f.setResizable(false);
    f.setVisible(true);
    f.setLayout(null);
    f.add(ploof);
}
}

"Ploofer" class:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;

import javax.swing.JPanel;


public class Ploofer extends JPanel{

private static boolean calledOnce = false;
private BufferedImage img = new BufferedImage(1000, 950,       BufferedImage.TYPE_INT_RGB);
static private Color backgroundColor = null;
private PumpkinPie pObj;

@Override
public void paintComponent(Graphics g){

    if(calledOnce == false){
        pObj = new PumpkinPie(this);
        calledOnce = true;
    }

    super.paintComponent(g);
    private Graphics2D g2d = img.createGraphics();

    if(pObj.colour != null){
            g2d.setColor(pObj.colour);
    }

    else{
            g2d.setColor(Color.BLACK);
    }

    if(pObj.setToBackgroundColor == true){
        pObj.colour = backgroundColor;
        pObj.setToBackgroundColor = false;
    }

    if(pObj.changeBackgroundColor == true){
        backgroundColor = pObj.colour;
        this.setBackground(backgroundColor);
        g2d.setBackground(backgroundColor);
        pObj.changeBackgroundColor = false;
        update(g2d);
        update(g);
    }

    if(pObj.wipe == true){
        g2d.clearRect(0, 0, img.getWidth(), img.getHeight());
        g.dispose();
        g2d.setBackground(Color.WHITE);
        g.drawImage(img, 0, 0, null);
        pObj.wipe = false;
        repaint();
    }

    if(pObj.draw == true){
        g2d.fillRect(pObj.x, pObj.y, 8, 8);
        pObj.draw = false;
    }



    if(img != null){
        g.drawImage(img, 0, 0, null);
    }
}

public BufferedImage getImage(){
    return img;
}

}

"PumpkinPie" class:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.Border;

public class PumpkinPie extends JFrame{

public int x;
public int y;
static public boolean draw = false;
static public boolean changeBackgroundColor = false;
static public boolean setToBackgroundColor = false;
static public boolean wipe = false;
static public Color colour = Color.WHITE;
Box box;

private JMenuBar menuBar;
private JMenu file, edit;
private JMenuItem save, saveas, exit, open, clear, changeBackground;
private JPanel colourButton;
private JButton saveButton;
private Icon eraser;
private JLabel eraserLabel;
private JFileChooser fc;

Ploofer ploof = new Ploofer();

public PumpkinPie(JPanel panel){
    super("SPLAT! SPLAT!");

    file = new JMenu("File");
    edit = new JMenu("Edit");
    box = Box.createHorizontalBox();

    menuBar = new JMenuBar();

    open = new JMenuItem("Open file");      
    save = new JMenuItem("Save");
    saveas = new JMenuItem("Save as...");
    exit = new JMenuItem("Exit");
    clear = new JMenuItem("Clear");
    changeBackground = new JMenuItem("Change background color");
    colourButton = new JPanel();
    eraserLabel = new JLabel();
    eraser = new ImageIcon(getClass().getResource("/Resources/Eraser.png"));
    fc = new JFileChooser();
    fc.setCurrentDirectory(new File("C:\\Users\\" + System.getProperty("user.name") + "\\Pictures"));
    fc.setDialogTitle("Choose a location...");
    saveButton = new JButton("Save");

    panel.setSize(1000, 950);
    panel.setLocation(0, 50);
    colourButton.setSize(50, 50);
    colourButton.setBorder(BorderFactory.createLineBorder(Color.BLACK));
    eraserLabel.setIcon(eraser);
    eraserLabel.setSize(50, 50);
    eraserLabel.setLocation(50, 0);

    this.setJMenuBar(menuBar);
    setLayout(new BorderLayout());

    menuBar.add(file);
    menuBar.add(edit);
    file.add(open);
    file.add(save);
    file.add(saveas);
    file.add(exit);
    edit.add(clear);
    edit.add(changeBackground);
    setLayout(null);
    add(colourButton);
    add(eraserLabel);

    MouseMoveHandlerer mouseMoveHandler = new MouseMoveHandlerer();
    MouseHandlerer mouseHandler = new MouseHandlerer();
    ButtonHandlerer buttonHandler = new ButtonHandlerer();

    panel.addMouseMotionListener(mouseMoveHandler);
    eraserLabel.addMouseListener(mouseHandler);
    clear.addActionListener(buttonHandler);
    colourButton.addMouseListener(mouseHandler);
    changeBackground.addActionListener(buttonHandler);
    save.addActionListener(buttonHandler);
    saveas.addActionListener(buttonHandler);
}

private class MouseMoveHandlerer extends MouseMotionAdapter{        
    public void mouseDragged(MouseEvent event){
        x = event.getX();
        y = event.getY();
        draw = true;
        repaint();
    }

}

private class MouseHandlerer extends MouseAdapter{
    public void mouseClicked(MouseEvent event){
        if(event.getSource() == colourButton){
            colour = JColorChooser.showDialog(null, "Choose a colour", colour);
            colourButton.setBackground(colour);
        }
        else if(event.getSource() == eraserLabel){
            setToBackgroundColor = true;
            repaint();
            colourButton.setBackground(colour);
        }
    }
}

private class ButtonHandlerer implements ActionListener{

    @Override
    public void actionPerformed(ActionEvent event) {
        if(event.getSource() == save){
        }
        else if(event.getSource() == saveas){
            if(fc.showSaveDialog(saveButton) == JFileChooser.APPROVE_OPTION){
                try{
                    ImageIO.write(ploof.getImage(), "PNG", new      File(fc.getSelectedFile().getPath() + ".png"));
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
        else if (event.getSource() == clear){
            wipe = true;
            repaint();
        }
        else{
            changeBackgroundColor = true;
            repaint();
        }
    }

}

}
Joshua Dannemann
  • 2,003
  • 1
  • 14
  • 34
Zyphicx
  • 299
  • 2
  • 13

3 Answers3

2

Can't really follow your painting logic, but a few comments:

  1. Don't invoke update(...) directly. Swing will invoke that method when appropriate. A painting method should only be concerned with painting logic.

  2. Don't use the paintComponent() method to create the BufferedImage. The point of using a BufferedImage is to draw the image once onto the BufferedImage and then just paint the BufferedImage in the paintComponent() method.

If you are going to recreate the BufferedImage every time, then you might as well just paint directly to the Graphics of the panel. Since you seem to have a bunch of variable that can change I would guess you should just do the painting directly to the panel and don't worry about the BufferedImage.

Except for the fact that the image is now completely black

Instead of trying to create the image in the paintCompnent() method you can create the BufferedImage as required. Check out the ScreenImage class. It will allow you to create an image of any Swing component.

Using this class the code to create/save the image would be something like;

BufferedImage bi = ScreenImage.createImage(yourPanel);
ScreenImage.writeImage(bi, "panel-image.png");

You might also want to check out Custom Painting Approaches to understand the differences between painting on a BufferedImage and painting in the paintComponent() method.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • The reason as to why I am using a BufferedImage is because when I use repaint() it just resets my panel, so I am sort of being forced to use a BufferedImage. Also, whenever I try to use ScreenImage I get a red underline which says "ScreenImage cannot be resolved", so I can't really get it to work :/ – Zyphicx Oct 09 '15 at 16:43
  • `I am sort of being forced to use a BufferedImage.` - you are never forced to do anything. I showed you two ways to do custom painting. You need to understand both approaches and decide which is best for you. Even if you do decide to use a BufferedImage you are doing it incorrectly. `ScreenImage cannot be resolved` - did you read link? Did you download the code provided? – camickr Oct 09 '15 at 17:23
  • Yeah, sorry about that, It's a bit late and I am sort of tired, even though that isn't a proper excuse. I am going to read everything properly and I'll report back if I get it working C: – Zyphicx Oct 09 '15 at 17:52
0

There might be some better solutions to the problem that you are having, maybe using a few tweak's to some of the code found in here would be helpful? I have never had to use Graphics2D in anything that I have personally used, but i'm thinking that is your best bet.

Community
  • 1
  • 1
ElectroMan
  • 156
  • 1
  • 13
0

So, I solved it! And I am posting the solution here if anyone needs help with the same thing! It is a bit of a cheat, but it works!

Basically I made a new class called "ScreenCapture"

    private class ScreenCapture{
        Dimension d = new Dimension(980, 896);
    public void takePicture(Point i, File file){
        BufferedImage image;
        try {
            image = new Robot().createScreenCapture(new Rectangle(i, d));
            ImageIO.write(image, "PNG", file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And whenever I want to call it I just do this:

try{
    ScreenCapture screenCap = new ScreenCapture();
    screenCap.takePicture(getScreenPlace(), new File(fc.getSelectedFile().getPath() + ".png"));
}catch(Exception e){
    e.printStackTrace();
}

The getScreenPlace() function only gets and changes the JFrame's screen position

private Point getScreenPlace(){
    this.setLocation(0, 0);
    Point location = getLocation();
    location.setLocation(location.getX()+3, location.getY()+100);
    return location;
}

I need to move the frame because if anything else is in the way it will be on the picture. Thanks to @camickr for helping me

(Also, the reason as to why I don't measure the screen's size is just because the way I have the program set up the screen size can't change)

Zyphicx
  • 299
  • 2
  • 13
  • Why did you create a new class? I gave you a class with that basic code (and other more efficient code). Your approach to get the location of the panel is a hack, since you hardcoded values. Those values can be different on other LAF (learn to write code that is LAF independent). Also you are using a Robot. The code I gave you is more efficient than using a Robot since the panel is drawn directly to the BufferedImage (which is better than having the Robot interact with a screen device). – camickr Oct 09 '15 at 20:57