BufferedImage nach dem Speichern immer schwarz

A.T.

Bekanntes Mitglied
Hallo zusammen,

ich lade von openstreetmap die Karten Tiles. Das funktioniert auch prima. Um nicht bei jedem Zoomwechsel oder beim verschieben der Karte alle Tiles neu laden zu müssen, wollte ich die Tiles cachen. Leider bekomme ich es nicht hin die Tiles zu speichern, bzw. die Bilder sind entweder immer alle schwwarz oder alle komplett transparent.
Meine Vermutung ist, das ich beim Umwandeln des Images in ein BufferedImage den falschen Typ auswähle. Aber igendwie scheint da gar nichts zu funktionieren.

Habe ein Testbeispiel gebaut mit dem man das Problem nachstellen kann:
Code:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class BufferedImageTest extends JFrame {

	public BufferedImageTest() {
		super("");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
		setLayout(new BorderLayout());
		Panel pan = new Panel();
		pan.setLayout(new BorderLayout());
		add(BorderLayout.CENTER, pan);
		setSize(500, 500);
		setVisible(true);
		pan.setVisible(true);
	}

	public static void main(String[] args) {
		new BufferedImageTest();
	}

	private class Panel extends JPanel {
		public void paintComponent(Graphics g) {
			super.paintComponent(g);
//			loadAndPaintComponent(g);
			loadSaveAndPaintComponent(g);
		}

		// Bild wird bei jedem paint neu geladen und angezeigt <--- Das funktioniert
		private void loadAndPaintComponent(Graphics g) {
			URL url = null;
			try {
				url = new URL("http://tile.openstreetmap.org/" + 16 + "/"
						+ 34320 + "/" + 21924 + ".png");
			} catch (Exception e) {
				e.printStackTrace();
			}
			g.drawImage(Toolkit.getDefaultToolkit().getImage(url), 0, 0, this);
		}

		// Bild nur einmal laden und speichern. Dann immer die Gespeicherte    <----- Diese Version funktioniert nicht!
		// Version verwenden.
		private void loadSaveAndPaintComponent(Graphics g) {
			String tmpPath = System.getProperty("java.io.tmpdir");
			String tmpSavePath = tmpPath + File.separator + "tmpSaveDir";
			File tmpSaveDir = new File(tmpSavePath);
			if (tmpSaveDir.exists()) {
				// Hier wird das schwarze Bild angezeigt. Das Bild im temp Ordner ist bereits schwarz. Laden scheint also zu funktioneren.
				g.drawImage(Toolkit.getDefaultToolkit().getImage(tmpSavePath+ File.separator + "tile.png"),0,0,this);
			} else {
				tmpSaveDir.mkdir();
				URL url = null;
				try {
					url = new URL("http://tile.openstreetmap.org/" + 16 + "/"
							+ 34320 + "/" + 21924 + ".png");
				} catch (Exception e) {
					e.printStackTrace();
				}

				Image image = Toolkit.getDefaultToolkit().getImage(url);

				BufferedImage bufferedImage = new BufferedImage(256, 256,
						BufferedImage.TYPE_3BYTE_BGR); //<------------------------------- hier schient das Problem zu liegen?!
				Graphics bIG = bufferedImage.createGraphics();

				bIG.drawImage(image, 0, 0, null);
				bIG.dispose();
				try {
					ImageIO.write(bufferedImage, "png", new File(tmpSaveDir + File.separator + "tile.png"));
				} catch (Exception e) {
					e.printStackTrace();
				}

				g.drawImage(image,0,0,this);
			}

		}

	}

}
Wäre super wenn mir einer sagen könnte welchen Type ich verwenden muss, bzw. wie ich die Tiles(PNG-Bilder) anders gespeichert bekomme.
Danke!
Gruß
A.T.
 
Diese Methode habe ich im i-Net gefunden. Hier gibts die z.B. Image to BufferedImage - Java - Source Code | DreamInCode.net obwohl ich den glaueb ich von einer anderen HP hatte.

Auf jeden fall ist das ja ziemlich aufwändig wenn ich das für jedes Bild durchführen muss. Kann man nicht irgendwie feststellen, welcher Typ es sein müsste und diesen dann einfach verwenden?
Wenn man auf bimage getType() aufruft erhält man eine 1 die einem nicht wirklich weiter hilft...

Code:
	public static BufferedImage toBufferedImage(Image image) {
	    if (image instanceof BufferedImage) {
	        return (BufferedImage)image;
	    }

	    // This code ensures that all the pixels in the image are loaded
	    image = new ImageIcon(image).getImage();

	    // Determine if the image has transparent pixels; for this method's
	    // implementation, see Determining If an Image Has Transparent Pixels
	    boolean hasAlpha = hasAlpha(image);

	    // Create a buffered image with a format that's compatible with the screen
	    BufferedImage bimage = null;
	    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
	    try {
	        // Determine the type of transparency of the new buffered image
	        int transparency = Transparency.OPAQUE;
	        if (hasAlpha) {
	            transparency = Transparency.BITMASK;
	        }

	        // Create the buffered image
	        GraphicsDevice gs = ge.getDefaultScreenDevice();
	        GraphicsConfiguration gc = gs.getDefaultConfiguration();
	        bimage = gc.createCompatibleImage(
	            image.getWidth(null), image.getHeight(null), transparency);
	    } catch (HeadlessException e) {
	        // The system does not have a screen
	    }

	    if (bimage == null) {
	        // Create a buffered image using the default color model
	        int type = BufferedImage.TYPE_INT_RGB;
	        if (hasAlpha) {
	            type = BufferedImage.TYPE_INT_ARGB;
	        }
	        bimage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
	    }

	    // Copy image to buffered image
	    Graphics g = bimage.createGraphics();

	    // Paint the image onto the buffered image
	    g.drawImage(image, 0, 0, null);
	    g.dispose();

	    return bimage;
	}
 
1. Bist Du Dir sicher, dass das Bild überhaupt gelesen werden kann? Eventuell nutzt Dein Rechner ja einen Proxy um ins Internet zu gehen.
==> mit einer lokalen Datei ausprobieren
2. Selbst wenn das Bild gelesen werden kann. Bist Du Dir sicher, dass das Bild bereits vollständig geladen ist wenn Du es rausschreibst? Toolkit.getImage() wartet nämlich nicht, bis das Bild im Speicher ist.
==> zum Einlesen der Bilder einen MediaTracker oder ImageIO.read() oder ImageIcon nutzen

Als Schnelltest einfach mal die Höhe oder Breite des eingelesenen Bildes ausgeben lassen, vermutlich steht dann bei Dir -1
 
Frage 1 kann ich definitiv mit ja beanworten. Frage 2 muss ich wohl eher mit nein beantworten. Habe den "Schnelltest" durchgeführt und der war negativ. Also -1...

Wenn ich ImageIO.read(...) verwende wird das alles ziemlich langsam. Da auf jedes einzelne Bild gewartet wird. Und es sind immer mindestens 25 Tiles eher mehr. Den MediaTracker muss ich mir mal angucken. Habe ich noch nie genutzt. Wäre halt praktisch wenn ich die Bilder sofort al Image anzeigen könnte und später speichern.
 
Wenn ich ImageIO.read(...) verwende wird das alles ziemlich langsam. Da auf jedes einzelne Bild gewartet wird. Und es sind immer mindestens 25 Tiles eher mehr.
Keine Ahnung wie ImageIO Bilder einliest und ob das die schnellste Methode ist, aber man kann Bilder erst anschauen oder abspeichern, wenn sie in den Arbeitsspeicher geladen wurden - und das kann manchmal dauern.
 
Wenn ich ImageIO.read(...) verwende wird das alles ziemlich langsam. Da auf jedes einzelne Bild gewartet wird. Und es sind immer mindestens 25 Tiles eher mehr. Den MediaTracker muss ich mir mal angucken. Habe ich noch nie genutzt. Wäre halt praktisch wenn ich die Bilder sofort al Image anzeigen könnte und später speichern.
Logik um Bilder aus dem Internet zu Laden, oder noch schlimmer, zu Speichern, hat in einer paintComponent auch nichts verloren.
Lade die Bilder asynchron und zeigst so lange zB Platzhalter.
 
@Wildcard bei dem Thema komme ich leider nicht weit. Gibts ein gutes Beispiel für das Thema oder ein Tutorial, wir fehlern irgendwie die richtigen Stichwörter...
 
Wenn ich ImageIO.read(...) verwende wird das alles ziemlich langsam. Da auf jedes einzelne Bild gewartet wird. Und es sind immer mindestens 25 Tiles eher mehr.

Threadpool anlegen, Queue mit den Adressen der zu ladenden Bildern erstellen, und die einzelnen Threads das Laden übernehmen lassen. Wenn das funktioniert, Thread-Anzahl optimieren (nicht zu hoch, ich würde mal im Bereich 5 bis 10 versuchen).
 
@Wildcard bei dem Thema komme ich leider nicht weit. Gibts ein gutes Beispiel für das Thema oder ein Tutorial, wir fehlern irgendwie die richtigen Stichwörter...

Lagere das Laden der Tiles in einen zusätzlichen Worker aus (Thread der im Hintergrund deiner App läuft).
Für jedes Tile dass du anforderst kannst du im GUI ja so lange einen Platzhalter anzeigen (z.b. ein Image, dass Lokal vorliegt). Wenn der Thread dann einen Tile fertig geladen (und gecached) hat, kannst du einfach das geladene Bild den vorher gesetzten Platzhalter ersetzen lassen.

Das Updaten der GUI mit geladenen Tiles köntnest du dann z.B. via Observer/Observables oder so realisieren, so dass immer wenn ein Tile "fertig" ist, ein Update gefeuert wird.
 

Neue Themen


Zurück
Oben