# Jpanel Graphik speichern



## me82 (16. Jul 2008)

Hallo zusammen,

bin in Grafikprogrammierung noch recht neu und wollte mir ein Programm schreiben mit dem ich
mit der Maus in einem JPanel malen kann. Daraufhin bin ich in diesem Forum auf diesen Thread 
http://forum.java.sun.com/thread.jspa?forumID=57&threadID=607073&start=1 gekommen.
Hab das ganze ein wenig umgeschieben und meinen Bedürfnissen angepasst.
Nun möchte ich das gemalte gerne speichern.

Mein erster Versuch war eine Speicherklasse st zu erstellen die das JPanel (bzw die JPanel, sollen mal mehrere werden) 
in einer Liste vorhält und Serializable implementiert.

```
objOut = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("test")));
objOut.writeObject(st);
```

Fehler dabei war dann :
    java.io.NotSerializableException: sun.awt.image.OffScreenImage
    ...
    bzw das Graphics2d Object

Alle weiteren Versuche z.B. das JPanel Serializable implementieren zu lassen oder über eine
getter Methode an das Image heranzukommen schlugen fehl

```
public BufferedImage getImage() {
        BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        img = (BufferedImage) createImage(getWidth(), getHeight());
        return img;
    }
```

Gleicher Fehler :     java.io.NotSerializableException: ...
Die Speicherklasse hab ich natürlich dementsprechend geändert.

Meine Frage ist also, ob es eine Möglichkeit gibt, sich beim Speichern das aktuelle offscreenImage zu holen (hoffe
ich habe das richtig ausgedrückt) und dieses (bzw. mehrere bei mehreren panels (das krieg ich dann schon hin  :wink: )) abzuspeichern und beim laden dann dem panel per 

```
setImage(...) {
this.image = newImage;
repaint();
}
```


das offsetImage zu übergeben, dieses zeichnen und dort dann weitermalen zu können?

Das Image als jpeg in eine Datei zu schreiben funktioniert, falls ich darauf aufbauen kann. Wollt ich aber eigentlich vermeiden. (aber wenn ich das dann im Panel zeichnen kann beim Laden solls mir recht sein).

Danke schon einmal und mfg
me82


----------



## Schnitter (16. Jul 2008)

Nuja, OffScreenImage implementiert Serializable eben nicht?!
Ich weiß jetzt grad nicht, ob das eine deirner Klassen ist, aber ich denk mal eher nicht.

(Warum heißt das bei dir sun.awt.image... und nicht java.awt.image?)

Jetzt mal ne kleine Vertständnisfrage: Willst du die Panels auf eine Grafik(BufferedImage) oder direkt auf den Frame zeichnen?

Als BufferedImage würd ich einfach ImageIO benutzen ums abzuspeichern.

Wenn dus auf den Frame zeichnen willst, würd' ich eifnach via xml die Position und die Größe abspeichern.


MfG


----------



## Me82 (16. Jul 2008)

Danke Dir erst mal für die schnelle Antwort,

Ich erklars mal ein wenig genauer,

ich habe einen JFrame, in diesem ein JTabbedPane und diesem füge ich meine DrawingPanels über ein Menü hinzu.
Soweit kein Thema.

Beim speichern laufe ich über alle panels mit

```
private void jmiSaveCompleteActionPerformed(java.awt.event.ActionEvent evt) {                                                
        ObjectOutputStream objOut = null;
        try {
        //jtpSites ist das JTabbedPane    
        for (int i = 0; i < jtpSites.getTabCount(); i++) {
                DrawingPanel p = (DrawingPanel) jtpSites.getComponentAt(i);
                //st is meine Speicherklasse
                st.addSite(p);
            }
            objOut = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("test.jpen")));
            objOut.writeObject(st);            
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                objOut.close();
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
```

So hab ich mir das vorgestellt, aber wie Du sagts wird Serializable nicht implementiert...

Ich poste noch mal die Speicherklasse, nix besonderes

```
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class SiteStorage implements Serializable {

    private List<DrawingPanel> allSites = new ArrayList<DrawingPanel>();
    
    public SiteStorage() {        
    }
    
    public List<DrawingPanel> getAllSites() {
        return allSites;
    }
    
    public void addSite(DrawingPanel dp) {
        allSites.add(dp);
    }
    
    public DrawingPanel get(int i) {
        return allSites.get(i);
    }
}
```

Und zu guter Letzt das JPanel zum zeichnen, wollte es eh in Codeschnipsel u. Projekte stellen also warum nid
gleich so... :wink: 


```
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.JColorChooser;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

public class DrawingPanel extends JPanel implements MouseListener, MouseMotionListener {
    
    //Zum testen nicht serialisieren
    private transient Image image;
    private transient Graphics2D g2d;
    
    private int brushSize = 3;
    private Point lastPoint;
    private Color paintColor = Color.BLACK;
    
    private JMenuBar bar = new JMenuBar();
    private JMenu options = new JMenu("Optionen");
    private JMenuItem increasePen = new JMenuItem("Stift vergrößern");
    private JMenuItem decreasePen = new JMenuItem("Stift verkleinern");
    private JMenuItem normalPen = new JMenuItem("Stift zurücksetzen");
    private JMenuItem color = new JMenuItem("Farbe wählen");

    public DrawingPanel() {
        addMouseListener(this);
        addMouseMotionListener(this);
        this.setLayout(new BorderLayout());
        increasePen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_MASK));
        increasePen.setMnemonic('+');
        increasePen.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                brushSize++;                
            }
        });
        decreasePen.setMnemonic('-');
        decreasePen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK));
        decreasePen.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if(brushSize > 1) {
                    brushSize--;
                }
            }
        });
        normalPen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK));
        normalPen.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {                
                brushSize = 3;
            }
        });
        
        color.setMnemonic('C');
        color.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
        color.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                paintColor = JColorChooser.showDialog(null, "Farbe auswählen", Color.BLACK);
            }
        });
        
        options.setMnemonic('O');
        options.add(increasePen);
        options.add(decreasePen);
        options.add(normalPen);
        options.addSeparator();
        options.add(color);
        bar.add(options);
        add(bar, BorderLayout.PAGE_START);        
    }

    public void mouseClicked(MouseEvent e) {        
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {        
    }

    public void mouseMoved(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
        lastPoint = e.getPoint();
        draw(lastPoint);        
    }

    public void mouseDragged(MouseEvent e) {
        if(!e.isShiftDown()) {
            double xDelta = e.getX() - lastPoint.getX();
            double yDelta = e.getY() - lastPoint.getY();
            double delta = Math.max(Math.abs(xDelta), Math.abs(yDelta));
            double xIncrement = xDelta / delta;
            double yIncrement = yDelta / delta;

            double xStart = lastPoint.getX();
            double yStart = lastPoint.getY();

            for (int i = 0; i < delta; i++) {
                Point interpolated = new Point((int) xStart, (int) yStart);
                draw(interpolated);
                xStart += xIncrement;
                yStart += yIncrement;
            }

            draw(e.getPoint());
            lastPoint = e.getPoint();
        }
        else {
            g2d.setColor(Color.WHITE);
            g2d.fillRect(e.getX(), e.getY(), 30, 30);
            drawGrid();
            repaint();
        }        
    }    

    //wird nicht verwendet. War einer meiner erfolglosen Tests
    public BufferedImage getImage() {
        BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        img = (BufferedImage) createImage(getWidth(), getHeight());
        return img;
    }
    
    //Ebenso nicht verwendet...
    public void setImage(Image i) {
        image = i;
        repaint();
    }
    
    private void drawGrid() {
        setColor(Color.BLACK);
        for (int i = bar.getHeight(); i < getHeight(); i += 30) {
            g2d.drawLine(0, i, getWidth(), i);
        }
        for (int i = 0; i < getWidth(); i += 30) {
            g2d.drawLine(i, 0, i, getHeight());
        }        
        setColor(paintColor);
    }
    
    private void setColor(Color newColor) {
        g2d.setColor(newColor);
    }
    
    private void draw(Point start) {
        setColor(paintColor);
        int x = start.x - (brushSize / 2) + 1;
        int y = start.y - (brushSize / 2) + 1;
        g2d.fillOval(x, y, brushSize, brushSize);
        repaint(x, y, brushSize, brushSize);
    }    

    public void clearPaint() {
        g2d.setColor(Color.white);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        drawGrid();
        
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (image == null) {
            image = createImage(getWidth(), getHeight());
            
            g2d = (Graphics2D) image.getGraphics();

            g2d.setColor(Color.white);
            g2d.fillRect(0, 0, getWidth(), getHeight());
            
            drawGrid();

            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

        }
        
        Rectangle r = g.getClipBounds();
        g.drawImage(image, r.x, r.y, r.width + r.x, r.height + r.y,
                r.x, r.y, r.width + r.x, r.height + r.y, null);
    }
}
```

Vllt kann ja jemand damit schon was anfangen und um zu Zeigen was ich machen möchte.
Wie man sieht würde ich gerne beim speichern alle Panels durchgehn und diese speichern.

@Schnitter
Hab verstanden warum das nicht klappt, danke Dir. Wollte nur zeigen wie ich mir das ganze gedacht habe  :wink: 
Seh nur leider noch keine Lösung...

Warum da sun.awt.image.OffScreenImage steht kann ich leider nid sagen...

Anfang Exception :

java.io.NotSerializableException: sun.awt.image.OffScreenImage
        at java.ibjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
        at java.ibjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
        at java.ibjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
        at java.ibjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
        ...
So rauskopiert.

Mfg
me82


----------



## Marco13 (16. Jul 2008)

Hm - willst du wirklich die Panels serialisieren? Das sollte man eigentlich nich (für langfistige Speicherung) machen. Da ist die Kompatibilität zwischen verschiedenen Java-Versionen nicht zugesichert. Wenn du nur die Bilder speichern willst, kannst du sowas machen wie
BufferedImage bi = new BufferedImage(...);
drawPanel.paintComponent(bi.getGraphics());
ImageIO.write(bi, ...);


----------



## me82 (16. Jul 2008)

Danke für deine Antwort, ich dachte die Panels zu serialisieren wäre die einfachste Lösung.
Ich werde es heute abend mal mit deinem Vorschlag versuchen die Images wegzuschreiben
und dann diese wieder zu laden.

mfg


----------



## me82 (17. Jul 2008)

Also ich habs nun doch so gemacht, dass ich die Panels als *.jpg speichere ( evtl noch in einer Datenstruktur zusammengefasst) und beim laden dann das Image pro Panel neu setzte ( paar Änderungen warn noch nötig aber alles halb so wild).

Die dazugehörigen Einstellungen wie Farbe oder Pinselgröße speichere ich dann separat entweder in einer
Property-Datei ( da hab ich mir mal ne Library geschrieben zum einfachen verwalten) oder in XML...mal schauen

Würde denn Interesse an so einem Programm bestehn, dann würde ich es erstens nicht nur für mich entwickeln, sondern besser  :wink: , und auch mal in die Projekte stellen. 
Meine Intention war es beim lernen, in meinem Fall Mathe, nicht immer 30 Seiten ausm Block verbrauchen zu müssen und das ganze auch mal mit anderen schnell austauschen zu können. Is natürlich Gewöhnungssache und nen Pen/Tablet is zu empfehlen aber kann ganz praktisch sein. :wink: 

Schreibt doch mal was ihr von so nem Ersatzblock haltet... :toll:  :bloed:
(Natürlich dann in besserer Form, hab ja dann nen Anreiz)

mfg
me82


----------



## Marco13 (18. Jul 2008)

Ohne deinen Enthusiasmus dämpfen zu wollen: Du solltest nicht darauf bauen, dass irgendjemand so ein Programm _braucht_. Die Idee, mehrere Seiten "zusammengepackt" zu speichern ist ganz lustig (obwohl ich bisher noch nichts gefunden habe, was einen Block und einen Bleistift ersetzen könnte) aber manchmal ist Programmieren ja auch ein bißchen Selbstzweck :wink:


----------

