Swing JComponent als BufferedImage speichern

Status
Nicht offen für weitere Antworten.

icarus2

Top Contributor
Hi

Ich habe gerade in ein bestehendes Projekt eingebaut, dass der Benutzer auswählen kann, dass er das Bild einer JComponent speichern kann, auf dem Desktop. Es wird da ein Ornder erstellt und das Bild wird auch korrekt als jpg abgespeichert.

Folgendes Problem:
Das Bild ist einfach schwarz. Ich kann mir nicht erklären warum das so ist.

Ich habe folgenden Code:
[Java]
BufferedImage image = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
[/Java]

Hinweis:
Das ganze läuft in einem eigenen Thread ab. In diesem Thread wird das Bild immer neu gezeichnet. Ist das vielleicht das Problem? Ich kann mir das nicht vorstellen, denn die JComponent wird erst neu gezeichnet, wenn das Bild bereits gespeichert wurde. Hat da jmd eine Idee?
 
Ein KSKB könnte helfen. Hast du mal (nur testweise) dort, wo du die Component in's Bild malst, stattdessen ein
imageGraphics.setColor(Color.RED);
imageGraphics.drawString("Haaaalllooo", 30, 30);
gemacht, um zu schauen, ob das Problem beim Zeichnen selbst oder beim Abspeichern liegt?
 
Also das Zeichnen funktioniert schon, so wie ich das sehe. Ich habe hier kurz ein vereinfachtest Beispiel gemacht:

Haupt-Klasse:
[Java]
package frame;

import java.awt.BorderLayout;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;

public class Main extends JFrame {

Container c;
TestPanel tp;

public Main(){

c = getContentPane();

tp = new TestPanel();
tp.setBorder(new TitledBorder("TestPanel"));

c.add(tp, BorderLayout.CENTER);

setSize(600, 600);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


}

public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run(){
new Main();
}
});
}

}
[/Java]

Und hier die von JComponent abgeleitete Klasse:
[Java]
package frame;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JComponent;

public class TestPanel extends JComponent implements KeyListener {

private File desktopDirectory; //path to desktop of the user

public TestPanel(){

desktopDirectory = new File(System.getProperty("user.home") + "/Desktop/images");
desktopDirectory.mkdir();

setFocusable(true);
addKeyListener(this); //if enter is pressed, a screenshot of the jcomponent is stored on the desktop
}

private void storeImage(){

BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
writeImageToDesktop(image, "TestImage");

}


public void writeImageToDesktop(BufferedImage image, String imageName){

try {
File outPutFile = new File(desktopDirectory.getAbsolutePath()+ "/" + imageName +".jpg");
ImageIO.write(image, "jpg", outPutFile);
}catch(IOException e){
e.printStackTrace();
}
}

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

g.setColor(Color.red);

g.fillOval(20, 20, 200, 300);
}

@Override
public void keyPressed(KeyEvent e) {}

@Override
public void keyReleased(KeyEvent e) {

if(e.getKeyCode() == KeyEvent.VK_ENTER){
storeImage();
System.out.println("image stored");
}

}

@Override
public void keyTyped(KeyEvent e) {}

}

[/Java]

Wenn du Enter klickst, dann sollte die JComponent als Bild auf deinem Desktop im Ordner images gespeichert werden. Das Bild ist dann allerdings nur schwarz.

Ich bin atm grad etwas überfordert iwie. Kannst du daraus erkennen, was nicht gut ist?
 
Also so wie ich das sehe, erstellst du einfach ein neues Bild mit den gewünschten Dimensionen und speicherst das dann ab. Du musst schon etwas auf dein bild raufzeichnen, sonst bleibt es natürlich leer. Eine einfache Möglichkeit wäre es sicher, mit der Robot Klasse einen Screenshot vom gewünschten Bereich zu machen und diesen abzuspeichern.
 
Danke, das mit der Robot Klasse hat mir sehr geholfen. Das Bild wird jetzt tatsächlich richtig gezeichnet =)

Ich muss jetzt nur noch herausfinden, wie ich die Koordinaten am besten abstimme. Aber das sollte ich schon noch hinbekommen, sonst frage ich hier nach.

Vielen Dank für die Hilfe =)
 
Neee, lass' das mal mit dem Robot. Dafür ist der eigentlich nicht da. Was du vorhast geht eher in Richtung von
Code:
    private void storeImage(){

        BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);

        [b]paint(image.getGraphics());[/b]

        writeImageToDesktop(image, "TestImage");

    }
(das Problem mit dem Robot könnte man mit den SwingUtilities lösen, aber das nur nebenbei...)
 
Also das mit dem paint() funktioniert ja schon mal recht gut =)

(Und ich darf sogar mal getGraphics() verwenden... gemäss aussagen, die ich hier im Forum gehört habe, dürfte ich mich jetzt also schon zu den Pros zählen ^^ ... naja, vielleicht auch doch nit ganz 😀)

Es gibt allerdings ein Problem:
Der Hintergrund (in der Standardfarbe von einer JComponent) wird jedes mal entweder schwarz oder weiss gezeichnet... immer abwechseln. Gibt es dafür einen bestimmten Grund?

Die Methode in einer externen Klasse sieht so aus:
[Java]
public void writeImageToDesktop(JComponent component, Point absoluteLocation, int width, int height, int solutionNumber){


BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
component.paint(image.getGraphics());


try {
File outPutFile = new File(desktopDirectory.getAbsolutePath()+ "/solution " + solutionNumber +".jpg");
ImageIO.write(image, "jpg", outPutFile);
}catch(IOException e){
e.printStackTrace();
}
}
[/Java]
 
(Und ich darf sogar mal getGraphics() verwenden... gemäss aussagen, die ich hier im Forum gehört habe, dürfte ich mich jetzt also schon zu den Pros zählen ^^ ... naja, vielleicht auch doch nit ganz 😀)
Dieses getGraphics ist nicht so böse wie das andere getGraphics. Obwohl... eigentlich wäre ja "createGraphics" besser. Und beim erstellten Graphics-Object sollte man auch "dispose" aufrufen, wenn man es nicht mehr braucht.

Zu den Hintergrundfarben: füll das Bild doch einfach zuerst mit einer Farbe auf, das könnte helfen.
 
So besser?
[Java]
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
component.paint(image.createGraphics());
[/Java]

Dumme Frage... aber wie kann ich ein BufferedImage einfach ganz auf Weiss setzen? Sry, hört sich vielleicht echt dumm an, aber ich habe gerade keine passende Methode dafür gefunden :/
 
So auf die Schnelle könnte folgendes funktionieren: (untestet!!)
Java:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.createGraphics();
g.setColor(Color.WHITE);
g.drawRect(0, 0, image.getWidth(), image.getHeight());
 
Hmm, nein, das funktioniert leider überhaupt nicht.

Kann mir jemand sagen, wie ich ein Bild erzeugen kann, das zu Beginn einen weissen Hintergrund hat?

*Edit:
Merkwürdigerweise funktioniert der Code von Marco einwandfrei, wenn ich die Klasse von JPanel ableite und nicht von JComponent. Hat wer ne Erklärung dafür?
 
Zuletzt bearbeitet:
Vermutlich ist die JComponent, die du auf das Bild zeichnest nicht opak und es wird kein Hintergrund gezeichnet (setOpaque(true), dann sollte es gehen). Ein JPanel ist anfangs immer opak.
 
Michael...'s Code zum füllen eines BufferedImage's mit weißer Farbe ist im Prinzip richtig, es muss nur fillRect statt drawRect heissen...
 
@andre111:
Achso, ja stimmt. Hatte das mal gelesen, aber wieder vergessen ^^

@Schandro:
Ok, ich werds nochmal versuchen. Aber es funktioniert eigentlich atm ganz gut =)

Danke euch.

Nur ne kurze andere Frage:
Ich habe bereits gegoogelt, aber nix brauchbares gefunden. Was ist die Standardhintergrundfarbe einer Swingkomponente? Ihr wisst schon, das hellgrau da.
 
Das hängt vom "Look and Feel" ab - d.h. ist unter Windows u.U. anders als unter Linux, und wenn man "Nimubs" einstellt anders als überall anders 😉

Man kann die einer bestimmten mit component.getBackground() bekommen, die allgemeine kann man evtl. irgendwo in den UIDefaults nachsehen...
 
Du kannst auch zuerst ein Bild erstellen, mit der Hintergrundfarbe (getBackground()) der Komponente füllen und dann die eigentliche Komponente (ob opak oder nicht) darauf zeichnen.

EDIT: Wobei das Probleme machen kann, wenn die Komponente die gezeichnet werden soll nicht opak ist und als Hintergrundfarbe z.B. blau hat und die Parent-Komponente das Standardgrau als Hintergrundfarbe hat, dann ist im Fenster als Hintergrundfarbe das Grau sichtbar, aber in dem gezeichneten Bild das Blau.
 
Zuletzt bearbeitet:
Also, es funktioniert jetzt alles wunderbar =)

Damit die JComponent wieder den Standardhintergrund bekommt mache ich jetzt einfach setBackground(null). Mit setOpaque(false) würde zwar den gewünschten Effekt auch erzielen, die JComponent darf aber wegen dem creageGraphics() nicht getOpaque() == false haben, da sonst der Hintergrund nicht grau, sondern schwarz ist.

Vielen Dank nochmal für eure tolle Hilfe.

Mfg ica
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben