Farbwerte eines Bildes in Pixel-Array abspeichern

Guten Abend,

ich habe im August mit Java-Programmierung angefangen und versuche mich momentan mit meinem ersten 2D-Spiel - um genauer zusein: Ich versuche es. Momentan habe ich ständig irgentwelche Fehler, die ich nicht beseitigt kriege:

Exception in thread „main“ java.lang.NullPointerException
at Test.getRGB(Test.java:38)
at Test.(Test.java:24)
at Test.main(Test.java:42)

Die folgende Klasse soll den Farbwert jedes Pixels in dem Bild „Pflastersteine.jpg“ im Ordner „res/textures“ in einen int-Array speichern:

}```
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

public class SpriteSheet {
private File path;
public final int SIZE;
public int[] pixels;

BufferedImage image;

public SpriteSheet(String path_c, int size) {
	SIZE = size;
	 path = new File(path_c);
	 pixels = new int [SIZE * SIZE];
	 load();
	 for (int y = 0; y < SIZE; y++) {
		 if (y > SIZE || y < 0) break;
		 for (int x = 0; x < SIZE; y++) {	     //Jeden einzelnen Pixel im array speichern
			 if (x > SIZE || x < 0) break;
			getRGB(x, y);
		 }
	  }
	
 }

private void load() {
	try {
		image = ImageIO.read(path);
		System.out.println("Bild wurde eingelesen!");
	 } catch (IOException e) {
		System.out.println("Bild nicht gefunden!");
	 }
 }

     public void getRGB (int x, int y) {
	pixels[x + y * SIZE] = image.getRGB(x, y);
  }

    public static void main(String[] args) {
	Test test = new Test("PflasterSteine.jpg", 100);
	System.out.println(test.pixels[50]);
}	

}```

Zum Test habe ich die Klasse aus dem Projekt genommen und ein Testprojekt erstellt, dass mi den 51. Pixel ausgeben soll.

Vielen Dank im Voraus :slight_smile:

MfG,
DVDB

Ich glaube er bekommt dein bild gar nicht.
Oder etwa doch?

benutz lieber ImegeIO.read(getClass().getResource(ralative_path));

Und ausserdem: wieso brauchst du in deinem aller aller ersten spiele projekt ein pixel array eines bildes? o.O

*** Edit ***

und wieso übergibst du size, wenn du die einfach aus dem bild bekommen kannst? image.getHeight() und getWidth()…

Danke für die Vorschläge. Sie haben allerdings nur den Fehlercode verändert. Es scheint alles darauf hinauszulaufen, dass der “path” falsch ist. Wie gebe ich ihn korrekt an?
Das Bild liegt in “res” Ordner unter “textures”, ist “/textures/PflasterSteine.jpg” dann richtig?

Der zeigt bei mir an:

Exception in thread “main” java.lang.NullPointerException
at sun.misc.MetaIndex.mayContain(Unknown Source)
at sun.misc.URLClassPath$JarLoader.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at java.lang.ClassLoader.getBootstrapResource(Unknown Source)
at java.lang.ClassLoader.getResource(Unknown Source)
at java.lang.ClassLoader.getResource(Unknown Source)
at java.lang.Class.getResource(Unknown Source)
at Test.load(Test.java:32)
at Test.(Test.java:16)
at Test.main(Test.java:42)

also - erstmal brauchst du dann natürlich kein File objekt sondern einen string, das ist aber klar oder?
getClass().getResource() sucht automatisch ab dem verzeichniss in dem die class liegt. (bzw dadrüber, ist nicht so leicht zu erklären)

zB bei folgendem system:

—Projekt
------src
---------textures
------------steine.png
------------grass.png
---------code
------------Loader.java
------------Start.java

würde man im Loader sagen: (der hier ab src sucht)

getClass().getResource("textures/steine.png");

[QUOTE=mymaksimus]also - erstmal brauchst du dann natürlich kein File objekt sondern einen string, das ist aber klar oder?
getClass().getResource() sucht automatisch ab dem verzeichniss in dem die class liegt. (bzw dadrüber, ist nicht so leicht zu erklären)

zB bei folgendem system:

—Projekt
------src
---------textures
------------steine.png
------------grass.png
---------code
------------Loader.java
------------Start.java

würde man im Loader sagen: (der hier ab src sucht)

getClass().getResource("textures/steine.png");[/QUOTE]Also “getClass().getRessource()” sucht pfadrelativ vom Bytecode der Klasse aus bzw. mit vorangestelltem “/” vom Rootverzeichnis der Jar (bzw. entsprechendem Klassenpfad) aus, in welchem sich die Klasse befindet.
“getClass().getClassLoader().getRessource()” hingegen sucht vom kompletten Classpath aus. Quellordner (src) werden bei der Ausführung in der IDE dem Klassenpfad hinzugefügt.
Würde man “getClass().getRessource(“textures/steine.png”)” nun aus einer Loader- oder Start-Instanz heraus aufrufen, würde die Ressource nicht gefunden, mit “getClass().getClassLoader().getRessource(“textures/steine.png”)” aber schon.

@Spacerat :

Hab zum Test mal…

		try {
			BufferedImage image = ImageIO.read(getClass().getClassLoader().getResource("textures/Pflastersteine.png"));
			System.out.println(image.getHeight());
		}
		catch(IOException e) {
			System.out.println("Fehler!");
		}
	}```

in mein Programm geschrieben. Protestiert zwar nicht, aber die Höhe gibt es mir trotzdem nicht.

MfG,
DVDB

Das heißt, es wird eine 0 und nicht „Fehler“ ausgegeben?

Weder “Fehler!” noch irgenteine Zahl.

Wenn nichts ausgegeben wird, wird die Methode auch nicht aufgerufen oder die Ausgabe landet an anderer Stelle.

Wenn nichts ausgegeben wird, wird die Methode auch nicht aufgerufen oder die Ausgabe landet an anderer Stelle.

Ich hab die Funktion in meinen kleinen Test nicht aufgerufen. BufferedImage image = ImageIO.read(getClass().getClassLoader().getResource("textures/Pflastersteine.png")); funzt.
Danke euch!

MfG,
DVDB

Noch einmal: wofuer brauchst du das?

Also:

In meinem Programm gibt es unter anderem folgende Klassen:


public class Screen {
	private int width, height;
	
	public int[] pixels;
	public int[] tiles = new int[100 * 100]; 
	
	SpriteSheet Pflastersteine = new SpriteSheet();
	
	public Screen(int width, int height) {
		this.width = width;
		this.height = height;
		pixels = new int [width * height];
	}	
	
	public void render() {
		for (int y = 0; y < width; y++) {
			//if (y < 0 || y > height) break;
			for (int x = 0; x < width; x++) {
				//if (x < 0 || x > width) break;				
				int tileIndex = (x / 100) + (y / 100 & 99) * 200;
				pixels[x + y * width] = Pflastersteine.pixels[(x&99) + (y & 99) * Pflastersteine.SIZE]; //Ordnet einem 100 x 100 Pixel großen Feld die Pixel des s
			}
		}
	}
		
	public void clear() {
		for (int i = 0; i < pixels.length; i++) {
			pixels** = 0;
		}
	}
}


public class SpriteSheet {
	private String path;
	public final int SIZE;
	public int[] pixels;
	
	public BufferedImage image;
	
	public SpriteSheet() {
		load();
		SIZE = 100;
		pixels = new int [SIZE * SIZE];
	}
	
	private void load() {
		try {
			BufferedImage image = ImageIO.read(getClass().getClassLoader().getResource("textures/Pflastersteine.png")); // mein ursprüngliches Problem 
			System.out.println("Bild wurde eingelesen!");
			int h = image.getHeight();
			int w = image.getWidth();
			image.getRGB(0, 0, w, h, pixels, 0, w);	
			
		} catch (IOException e) {
			System.out.println("Bild nicht gefunden!");
		}
	}
}

Bisher sagt er nur “Bild wurde eingelesen”, das Verarbeiten läuft noch nicht ganz. Render() und Clear() werden in der Klasse “Game” in der Funktion “running” ausgeführt. Das Konzept hab ich von dem Youtuber “theCherno”
allerdings kann arbeite ich an einem komplett anderen Spiel als er, sodass ich mich nicht mehr an ihn richten kann. Die beiden Klassen sind nur Prototypen.

MfG,
DVDB

eehhh… Zeile 41 vs 51. Da kann man später mal leicht drüber stolpern. Ist nach aktuellem Code zwar irrelevant, aber Du hast in Zeile 41 eine Instanzvariable image die nie initialisiert wird und da sie auch nicht genutzt wird würde ich die Zeile gleich mal löschen…

[quote=DVDB]public void render() { for (int y = 0; y < width; y++) { //if (y < 0 || y > height) break; for (int x = 0; x < width; x++) { //if (x < 0 || x > width) break; int tileIndex = (x / 100) + (y / 100 & 99) * 200; pixels[x + y * width] = Pflastersteine.pixels[(x&99) + (y & 99) * Pflastersteine.SIZE]; //Ordnet einem 100 x 100 Pixel großen Feld die Pixel des s } } }[/quote]
So ganz verstanden was da passieren soll habe ich nicht:

  • Sollte die äußerste Schleife (y) nicht bis height laufen?
  • tileIndex wird zwar berechnet aber nie benutzt, mal abgesehen davon, dass da sowieso immer 0 raus kommt.
  • Was in der pixels[x + y * width] = Pflastersteine.pixels[(x&99) + (y & 99) * Pflastersteine.SIZE]; tatsächlich passieren soll, ist mir noch völlig unklar. Warum werden einzelene Werte aus dem Bild mehrfach übernommen andere überhaupt nicht? Nach welchen Kriterien soll das ablaufen?

Was hat es mit dem 100 x 100 Pixel Feld auf sich? Wäre da ein int[100][100] evtl. praktischer als ein int[10000]?

[QUOTE=_Michael;70433]Was hat es mit dem 100 x 100 Pixel Feld auf sich? Wäre da ein int[100][100] evtl. praktischer als ein int[10000]?[/QUOTE]In diesem Falle nicht! Das int-Array lässt sich bei “.setRGB()” nur eindimensional übergeben.
Das mit dem “& 99” ist völlig überflüssig, zumindest wenn es in der Art “% 100” verwendet wird.

Was genau soll dieses & 99 bewirken? Ein %100?

99 binär entspricht

64 + 32 + 2 + 1

also 01100011

Wenn du nun irgendeine Zahl damit bitweise ver-&-dest, etwa die Zahl 24, was kommt dann heraus?

24 00011000
99 01100011

00 00000000

Willst du das wirklich?

Mich würde die Antwort auf die Frage “Willst du das wirklich” auf einer anderen Ebene interessieren: Wozu kopierst du da diese Arrays ineinander? Soll da am Ende wieder ein Bild rauskommen?

Das Programm soll den gesammten Screen mit diesen Bildern zupflastern. Diese Bilder wandern nach unten und von oben sollen neue kommen.
Dabei möchte ich die Freiheit haben, zu entscheiden welche Bilder benutzen möchte (zwischen Pflastersteinen ein Gullideckel-tile) und möchte abrufen, ob, der
Held sich auf dem jeweillige Tile befindet (Bei Lava würde er dann Damage-over-Time nehemen).

[QUOTE=DVDB]Das Programm soll den gesammten Screen mit diesen Bildern zupflastern. Diese Bilder wandern nach unten und von oben sollen neue kommen.
Dabei möchte ich die Freiheit haben, zu entscheiden welche Bilder benutzen möchte (zwischen Pflastersteinen ein Gullideckel-tile) und möchte abrufen, ob, der
Held sich auf dem jeweillige Tile befindet (Bei Lava würde er dann Damage-over-Time nehemen).[/QUOTE]Ginge das mit den Tiles als BufferedImage und Graphics2D nicht viel besser? Mit einem erweiterten BI (bzw seit 1.7 mit einem eigenen Raster/SampleModel realisierbar) könnte man sogar die Kollisionsabfrage in das Samplemodel verlegen.

Diese Bilder kannst du erstmal einfach Zeichnen, mit graphics.drawImage(…). Warum willst du die Pixeldaten händisch in einen Array packen?

Diese Bilder kannst du erstmal einfach Zeichnen, mit graphics.drawImage(…)

Du hast recht, werde es mal auf dem unkomplizierteren Wege versuchen.

Mit einem erweiterten BI (bzw seit 1.7 mit einem eigenen Raster/SampleModel realisierbar) könnte man sogar die Kollisionsabfrage in das Samplemodel verlegen.

Sagt mir, als Java-Neuling nichts. Wie benutzt man es? Ist das sowas:

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();