int-Array zu BufferedImage (performance?)

pexx

Mitglied
hi leute..

ein kommilitone und ich basteln seit einiger zeit an einer bildverarbeitungs-sw.

der aufbau is kurz erklärt: die bildquelle (cam/film/bild) ist ein BufferedImage, aus dem generieren wir uns ein eindimensionales int-array der länge width * height.

der ausgabethread holt sich jetzt das aktuelle bild als int-array, schickt es durch die filterkette, wandelt das resultierende int[] in ein BufferedImage um und zeichnet ggf. irgendwelche Overlays drauf.

in der paint() methode wird nun immer das image aufs frame gezeichnet.

beim optimieren dieses vorgangs is mir aufgefallen das die schnellste möglichkeit das int[] in ein BI zu bekommen ein instanziieren eines neuen objekts war ???:L

Java:
public void draw(int[] picture) {								
	image = new BufferedImage(
		dcm, 
		WritableRaster.createWritableRaster(sample, new DataBufferInt(picture, length), ZERO_POINT),
		false, 
		null
	);

	// ....

	repaint();
}

dieser versuch benötigt ~20ms um das bild zu zeichnen.
//EDIT: das ist die dauer in der die bilder letztendlich im Frame erscheinen

wie man weiss is das anlegen neuer objekte in zeitkritischen abschnitten nich unbedingt die beste vorgehensweise, also hab ich versucht nur die daten des BI zu überschreiben.

Java:
//...
image.setData(WritableRaster.createWritableRaster(sample, new DataBufferInt(picture, length), ZERO_POINT));
//...

komischerweise benötigt das mit ~40ms etwa doppelt soviel zeit ???:L

andere versuche direkt im Buffer eines vorher instanziierten BI herum zu fummeln hatten irgendwie garkeinen effekt (schwarzes bild).

tja und direkt den puffer des quellbildes können wir leider auch nicht manipulieren da die filterkette die größe des bildes (und damit die länge des arrays) ändern könnte und ggf. auch mehrere ausgabethreads gleichzeitig laufen.

---

kennt jemand eine möglichkeit den vorgang zu beschleunigen (int[] --> BufferedImage) ??


mfg
 
Zuletzt bearbeitet:

Steev

Bekanntes Mitglied
Ich würde direkt für die Bildverarbeitung das WriteableRaster des BufferedImages verwenden, so arbeitest du immer mit demselben Objekt und erzeugst nicht ständig neue Objekte.

Das der zweite Vorgang noch mehr zeit benötigt ist ja eigendlich logisch, weil ja noch mehr (unnötige) Objekte erstellt werden.
 

pexx

Mitglied
danke für die schnelle antwort

Das der zweite Vorgang noch mehr zeit benötigt ist ja eigendlich logisch, weil ja noch mehr (unnötige) Objekte erstellt werden.

die objekte werden doch beim ersten vorgang genauso angelegt. WritableRaster und DataBufferInt, nur BufferedImage kommt noch hinzu und trotzdem dauert das alles nicht so lang wie bufferdImage.setData(...)

----

zunächst mal waren die zeitangaben mit 20ms humbuck .. hab an der falschen stelle gemessen.

die ausgabe hab ich jetzt derart verändert das dass BufferedImage einmal beim start des threads erstellt und mit jedem frame manipuliert wird.

Java:
public void draw(int[] picture) {

	for (int i = picture.length - 1; i >= 0; i--)
		image.getRaster().getDataBuffer().setElem(i, picture[i]);
				
	filter.drawOverlay(image.getGraphics());
	repaint(sleeptime);		
}

im gegensatz dazu die originalidee mit jedem frame ein neues BufferedImage anzulegen und zu zeichnen

Java:
public void draw(int[] picture) {		
	image = new BufferedImage(
		dcm, 
		WritableRaster.createWritableRaster(sample, new DataBufferInt(picture, length), ZERO_POINT), 
		false, 
		null
	);
		
	filter.drawOverlay(image.getGraphics());
	repaint(sleeptime);		

}

---

ich hab mal die performance untersucht. das anlegen eines neues BI + DataBufferInt + WritableRaster dauert etwa 60 000 nanosekunden.

das iterieren über das komplette array und überschreiben des rasters mit den werten dagegen dauert.. jetzt kommts.. etwa 3milisekunden. also ist damit etwa 50x langsamer.


verrückte welt.. ich werd das mal noch genauer auseinander nehmen. hat jemand ne idee wie das sein kann?


trotzdem die frage bleibt, wie schreibt man RGB-Daten im int-format am effektivsten in ein BufferedImage?


mfg
 
Zuletzt bearbeitet:

Steev

Bekanntes Mitglied
Ja, erstmal scheint die Methode mit einem neuen Objekt schneller zu sein, aber warte einfach mal ein paar Minuten, dann wirst du schon merken, wie die Performance in die Knie geht. Erstmal wird das alte Objekt einfach verworfen und ein neues Objekt wird angelegt. Das geht natürlich schneller, als das abändern eines bestehenden. Aber nach einiger Zeit schaltet sich dann der GarbageCollector hinzu und fängt an deinen Objektmüll aufzuräumen. Und dabei geht dann die Performance extrem in die Knie. Als fährt man im Schnitt mit dem Ändern eines bestehenden Objektes besser.

Gruß
Steev
 

pexx

Mitglied
Ja, erstmal scheint die Methode mit einem neuen Objekt schneller zu sein, aber warte einfach mal ein paar Minuten, dann wirst du schon merken, wie die Performance in die Knie geht.

Danke für den Tipp... ich hab den memory mal auf 8mb gesetzt und mit visualvm zugeschaut wie der heap voller und voller wurde. nach ner minute oderso war der heap dann auf 80% und es hat im 5sekundentakt ein lustiges zackenmuster gemalt. also wohl immer wenn der GC den müll raus bringt.

wenn das geschieht ist kurzzeitig ein performanzverlust zu erkennen. das erstellen des BI dauert dann etwa 1.000.00 nanosek (also etwa 2x solang). da der ausgabethread aber eh die meiste zeit (0.05s also 60FPS) pennt ist visuell kein unterschied zu merken.

ich bin ja auch kein fan davon den speicher so voll zu müllen, aber selbst wenn der GC vollauf beschäftigt ist, macht diese methode ihren job immernoch 20x schneller als das pixelweise neusetzen des Rasters.

ich vermute mal das der performanzgewinn hier daraus entsteht, dass der datenpuffer vom int-array nicht durchiteriert werden muss sondern einfach das orginalarray zum erstellen verwendet wird. is aber nur ne idee, muss mal ins jdk src schaun.

vlt. gibts ja ne möglichkeit um die referenz auf den datenpuffer im raster zu ändern?

mfg
 

Marco13

Top Contributor
Hmja, ich hatte da auch mal rumprobiert... Erstmal vorneweg: Zeiten im Nano- und auch Millisekundenbereich zu messen ist meistens nicht verläßlich. Und man kann davon ausgehen, dass der Garbage Collector ziemlich clever ist, und nicht sooo schnell zum Flaschenhals wird.

Ich hatte damals auch versucht, einen Array in ein BufferedImage zu packen. Bei mir war es ursprünglich ein byte-Array. Was man zumindest schonmal sagen kann, dass sowas wie image.setRGB(...) meistens SEHR schlecht ist, weil dort erstmal ein ganzer Rattenschwanz von Konversionen von ColorModel & Co kommt, bevor die Daten dann doch 1:1 im DataBuffer landen. Zwischendurch hatte ich auch den Versuch gemacht, dass ich direkt in den byte[]-Array geschrieben habe, der im BufferedImage im Raster im DataBuffer lag. (Also wirklich denSELBEN byte-Array). Man sollte eigentlich meinen, dass das schnell geht - es schien aber (ohne, dass ich eine Möglichkeit sähe, das unumstößlich zu verifizieren) so, dass das eigentliche Zeichnen eines BufferedImages mit sowas wie g.drawImage(...) bei einem BufferedImage, das einen byte[]-Array enthielt DEUTLICH langsamer war, als bei einem BufferedImage, das einen int[]-Array enthielt. (Das nur als Randnotiz - du hast einen int[], deswegen stellt sich die Frage bei dir nicht - aber vermeintliche Optimierungen an der einen Stelle (an der man irgendwelche Millisekunden-Zeiten mißt) können eben u.U. mehr als aufgefressen werden, wenn die Optimierung an der einen Stelle eine Verlangsamung an einer anderen Stelle nach sich zieht (die man ggf. gar nicht messen KANN)).

Mein letzter Stand (nach vielen Tests) ist, dass wenn man sicher sein kann, dass der BufferedImage.TYPE irgendwas mit "_INT" ist, es schon sehr schnell ist, sowas zu machen wie
Code:
int data[] = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
for (...) { data[i] = source[i]; } // Oder System.arraycopy
Allerdings kann es sein, dass bei meinen vielen Tests ein Neu-Erzeugen des Bildes in der beschriebenen Form nicht dabei war, und es ist gut möglich, dass das deutlich schneller ist. Ein Objekt zu erzeugen (Speicher zu allokieren) ist zwar aufwändig, aber vermutlich nicht so aufwändig wie durch einen Array mit 1 Million Elementen zu laufen.

Mit letzter Bestimmtheit etwas dazu zu sagen ist aber schwierig. Java führt intern ziemlich viele Optimierungen durch, zu denen man Informationen findet wenn man nach sowas wie "managed image bufferedimage" usw. sucht. Teilweise werden Bilder im VRAM gehalten, aber dieses Halten im VRAM kann unmöglich gemacht werden, wenn man direkt auf die Pixel zugreift. Es gibt verschiedene JVM-Flags die dieses Verhalten beeinflussen, und es hängt STARK von der verwendeten Version der JVM ab... Alles nicht so einfach ;)
 

Steev

Bekanntes Mitglied
ich vermute mal das der performanzgewinn hier daraus entsteht, dass der datenpuffer vom int-array nicht durchiteriert werden muss sondern einfach das orginalarray zum erstellen verwendet wird. is aber nur ne idee, muss mal ins jdk src schaun.

vlt. gibts ja ne möglichkeit um die referenz auf den datenpuffer im raster zu ändern?

Ja genau da liegt der Performancegewinn. Um die Referenz auf den Datenpuffer zu ändern müsste man den Source bzw. sein eigenes BI schreiben. Das ist aber prinziepiell unnötig, da eine Referenzänderung auf einen anderen, neuen Datenpuffer ja gleichzeitig ein verwerfen des alten bedeutet. Was einer neuinitialisierung gleich kommt. Da ja das größte am BI der Datenpuffer ist.

Gruß
Steev
 

Marco13

Top Contributor
Oh, da war ich mal wieder langsam - aber zur letzten Frage: Man kann in seiner Anwendung prinzipiell denSELBEN Array verwenden, wie der, der im DataBuffer des BufferedImages liegt - aber nachträglich ändern kann man ihn (ohne Hack-arounds) nicht...

Musst du den picture[]-Array jedes mal übergeben, oder könnte nicht da, wo dieser Array herkommt, IMMER derSELBE Array verwendet werden (der auch einmal im Image liegt)?
 

pexx

Mitglied
@marco13:

das die getRGB methode schnecke ist hab ich schon gemerkt ;) hier bietet es sich wirklich an statt getRGB(..) den DataBuffer mit bytes selbst (iterativ) in ein rgb-int-array zu überführen.


Java:
public static final int[] byteToInt (final byte[] b, final int[] ret) {
	for (int i = ret.length-1; i >= 0; i--) 
		ret[i] =  byteToRGBInt (b, i*3);		
	return ret;
}

public static final int byteToRGBInt (final byte b[], final int i) {
	return ((b[i+2] & 0xFF) << 16) | ((b[i+1] & 0xFF) << 8) | (b[i] & 0xFF);
}

// ...
byteToInt(((DataBufferByte)graph.getImage().getRaster().getDataBuffer()).getData(), imageData);

//EDIT: resultat war ein ~10facher performanzgewinn (von 30ms zu 3ms)
--

Musst du den picture[]-Array jedes mal übergeben, oder könnte nicht da, wo dieser Array herkommt, IMMER derSELBE Array verwendet werden (der auch einmal im Image liegt)?

das selbe array kann ich leider nicht verwenden da meine filter dann nichmehr funzen. die können das bild z.B. scalen und damit die größe des arrays ändern oder das bild in einzelne kanäle (RGB) splitten, einzeln verändern und später wieder zusammen führen. kurzum, ich kann nicht sicher gehen das dass array nach der filterung noch das selbe ist.
--
gut, kann sein das dass timing fehlerhaft ist. dazu hab ich ne klasse gebaut welche die differenz zwischen stop() und start() in einem ringbuffer hällt und daraus den mittelwert berechnet. hab die kapazität auf 60 gestellt (da ich mit 60fps abfrage missts also immer auf eine sekunde) und auch nach 10min laufzeit ist ein konstantes verhalten zu beobachten. 60k ns zum erstellen des BI und 3ms zum überschreiben des rasters.
--
diese hacks von denen du da sprichst würden mich übrigens mal interessieren :)

-----

naja ich hab die ganze sache jetzt dahingehend verändert das ich die 3ms in kauf nehme.. irgendwie passts mir auch nicht in den kram ständig neue BIs zu erstellen. der GC wirds mir hoffentlich danken.

die 3ms fallen wohl nicht mehr sonderlich auf bei filtern (Faltung, Median, Adaptiver Contrast, oderso) die gerne mal 50ms benötigen.


ich werd trotzdem mal weiter rumoptimieren, vlt. beschäftige ich mich auchmal mit volatileimage. vlt kann man damit noch ein paar fps rauskitzeln!? ???:L


mfg
 

Marco13

Top Contributor
diese hacks von denen du da sprichst würden mich übrigens mal interessieren :)

Mit Reflection kommt man auch an private Variablen dran .... :oops:

ich werd trotzdem mal weiter rumoptimieren, vlt. beschäftige ich mich auchmal mit volatileimage. vlt kann man damit noch ein paar fps rauskitzeln!? ???:L
Das das VolatileImage nur (und auch nur potentiell) schneller ist, weil es im VRAM gehalten wird, sehe ich da nicht so viele Chancen, aber ... versuchen kann man's ja...
 

Steev

Bekanntes Mitglied
Bei dem VolatileImage kommt man aber nicht an ein WriteableRaster ran, man kann nur über die bekannten Methoden in ein VolateileImage reinschreiben. Sozusagen write only. Jedenfalls habe ich es noch nicht hinbekommen, falls es doch gehen sollte, würde mich das sehr interessieren :)
 

pexx

Mitglied
...nur über die bekannten Methoden in ein VolateileImage reinschreiben. Sozusagen write only.

also nur über des Graphics-Object?! das is madig :D

--

@marco13:
mit reflection was? :eek: mein swe-prof würd mich dafür lynchen aber gute idee, ich probiers mal :toll:


ps. ist die seite nur in meinem browser so zerrissen?
 

pexx

Mitglied
so wie's aussieht funzt das überschreiben des Rasters 1zu1 mit den werten des picture-arrays doch nicht richtig. einige filter haben jetzt die angewohnheit ein schwarzes bild zu erzeugen. ich nehm mal an das da irgendeine konvertierung des formats fehlt ;(

ich geh der sache mal nach..
 

pexx

Mitglied
hey leute..

ich hab jetzt alle mir denkbaren möglichkeiten ausprobiert um die Daten eines TYPE_INT_RGB BufferedImages mit einem int-array zu überschreiben.

-- 1 --
zunächst mal, die setRGB()-Methode ist tierisch lahm (~20ms). warum dem jetzt so ist oder was die methode für vorzüge hat, hab ich nicht näher nachgeforscht.

Java:
image.setRGB(startX, startY, w, h, rgbArray, offset, scansize)

-- 2 --
als nächstes hab ich versucht den Raster des bildes zu überschreiben. auch hier sehr langsam (~20ms). wie man sieht werden hier bei jedem durchlauf zusätzlich ein neues WritableRaster und ein DataBufferInt angelegt.

Java:
// length  : int mit width*height
// picture : int[] mit rgb-daten
// sample : SampleModel zuvor generiertes SampleModel
// ZERO_POINT : Point (0,0)  glaub hier kann man auch null nehmen

image.setData(WritableRaster.createWritableRaster(sample, new DataBufferInt(picture, length), ZERO_POINT));

-- 3 --
so jetzt wirds ein wenig schneller. das überschreiben der daten direkt im Raster dauerte etwa 3ms.
auch hier wird zusätzlich noch ein WritableRaster & DataBufferInt erzeugt.

Java:
image.getRaster().setDataElements(0, 0, WritableRaster.createWritableRaster(sample, new DataBufferInt(picture, length), ZERO_POINT));

-- 4 --
wie schon zu beginn des threads erwähnt war das schnellste immer ein neues BufferedImage zu erstellen. hat allerdings den nachteil das der speicher zumüllt. dauerte übrigens <1ms.

Java:
image = new BufferedImage(
     dcm, 
     WritableRaster.createWritableRaster(sample, new DataBufferInt(picture, length), ZERO_POINT),
     false, 
     null
);

-- 5 --
so last but not least meine finale variante. auch hier dauert das ganze ~1ms jedoch werden keine weiteren objekte angelegt.
//EDIT: wichtig ist hier noch die flush()-methode aufzurufen vor jedem überschreiben des BI, sonst sieht man nur schwarz :D

Java:
image.flush();
int[][] bankdata = ((DataBufferInt) (image.getRaster().getDataBuffer())).getBankData();		
System.arraycopy(picture, 0, bankdata[0], 0, length);


so vlt. hilfts ja mal jemandem

@Marco13:
übrigens ist der versuch (mittels reflection) die referenz des int[] zu ändern immer in einem fatal error der vm geendet :shock:

damit ist der thread wohl erledigt.

mfg
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
A Spielfelder erstellen mit Jogl Java durch ein Koordinaten Array Spiele- und Multimedia-Programmierung 1
A Multidimensionler Array - Elemente vergleichen (TicTacToe) Spiele- und Multimedia-Programmierung 4
T LWJGL: Terrain-Texturen / 2D-Array in Shader? Spiele- und Multimedia-Programmierung 2
J Frage zum Einlesen einer .wav in ein byte Array Spiele- und Multimedia-Programmierung 3
B klassenobjekte als Array Spiele- und Multimedia-Programmierung 3
Seikuassi gluLookAt mit glMultMatrix und Array Spiele- und Multimedia-Programmierung 3
Androbin mehr-dimensionale(n) Array(s) "drehen" Spiele- und Multimedia-Programmierung 8
N [SLICK] Rectangle in Array Speichern Spiele- und Multimedia-Programmierung 3
L Slick-Image aus byte-array erzeugen Spiele- und Multimedia-Programmierung 2
A Patter Array Eintrag vergleichen geht nicht!!! Spiele- und Multimedia-Programmierung 3
J A*, Array und KI Spiele- und Multimedia-Programmierung 5
L Tile Map als Array? Spiele- und Multimedia-Programmierung 23
O In Array Pulk von gleichen Zahlen finden. Spiele- und Multimedia-Programmierung 4
K Schiebepuzzle Array Zufallszahlen Problem Spiele- und Multimedia-Programmierung 8
R Schach, Array und GUI Verknüpfung, enum Spiele- und Multimedia-Programmierung 16
B Array und Parameter Spiele- und Multimedia-Programmierung 38
S Methode verändert mein Eingabe Array oO Spiele- und Multimedia-Programmierung 6
Z Kugeln aufgrund von Daten aus einem Array verschieben Spiele- und Multimedia-Programmierung 2
D Array aus Objekten Spiele- und Multimedia-Programmierung 2
T Bild als mehrdimesionaler Array Spiele- und Multimedia-Programmierung 4
H Jpeg Bildinformation in ein Char Array speichern? Spiele- und Multimedia-Programmierung 4
J Array/Matrix auswerten. Spiele- und Multimedia-Programmierung 6
C Array 'Wiederbenuezten' - Map Spiele- und Multimedia-Programmierung 4
B Ein Array für Buttons Spiele- und Multimedia-Programmierung 8
T 2 Dimensionales Array herausschreiben Spiele- und Multimedia-Programmierung 4
N Array mit verschiedenen Klassen Spiele- und Multimedia-Programmierung 15
G 2 dim. Pixel Array in AWT? Spiele- und Multimedia-Programmierung 7
B BufferedImage Position Spiele- und Multimedia-Programmierung 8
B BufferStrategy zu BufferedImage? Irgendwie? Spiele- und Multimedia-Programmierung 2
D getsubimage aus BufferedImage und Rueckwandlung in ein ImagePlus bild Spiele- und Multimedia-Programmierung 0
V BufferedImage[] aus anderer Classe auslesen Spiele- und Multimedia-Programmierung 2
F [JMyron] Bild von int[] zu BufferedImage Spiele- und Multimedia-Programmierung 2
Q BufferedImage vs. Heap Space ‒ Warum wird der Speicher nicht freigegeben? Spiele- und Multimedia-Programmierung 6
M Performance Problem bei BufferedImage Spiele- und Multimedia-Programmierung 7
R BufferedImage > Integer.MAX_VALUE Spiele- und Multimedia-Programmierung 9
M Pixel eines BufferedImage bearbeiten (Performance) Spiele- und Multimedia-Programmierung 23
G BufferedImage -> Farbe wechselt willkürlich Spiele- und Multimedia-Programmierung 15
agentone BufferedImage transparent löschen Spiele- und Multimedia-Programmierung 12
radiac Stringtext mit BufferedImage Textur. Spiele- und Multimedia-Programmierung 6
kowa BufferedImage und Antialias Spiele- und Multimedia-Programmierung 2
T BufferedImage#setRGB #getRGB zu langsam Spiele- und Multimedia-Programmierung 4
J Zeichnen in BufferedImage und dieses in Datei speichern Spiele- und Multimedia-Programmierung 2
A Image in BufferedImage konvertieren Spiele- und Multimedia-Programmierung 2
Z Transparenz in BufferedImage Spiele- und Multimedia-Programmierung 8
Z JME - Rendering in BufferedImage Spiele- und Multimedia-Programmierung 14
A Bildbereich als BufferedImage Spiele- und Multimedia-Programmierung 3
M BufferedImage blass machen Spiele- und Multimedia-Programmierung 5
Lulumann6 BufferedImage in VolatileImage casten Spiele- und Multimedia-Programmierung 10
F BufferedImage verursacht OutOfMemoryError Spiele- und Multimedia-Programmierung 11
P Rotation von BufferedImage (Affine Transformation) Spiele- und Multimedia-Programmierung 7
S Image to BufferedImage Spiele- und Multimedia-Programmierung 3
K VideoPlayer: Xuggler seekkeyframe extrem schlechte Performance.. Spiele- und Multimedia-Programmierung 3
pcfreak9000 "Allgemeine" Performance verbessern (LWJGL 2) Spiele- und Multimedia-Programmierung 2
turing OpenGL / Jogle Code Reveiw zur Performance Verbesserung Spiele- und Multimedia-Programmierung 1
F Massive FPS-Schwankungen, schlechte Performance Spiele- und Multimedia-Programmierung 3
J Java Game performance Probleme Spiele- und Multimedia-Programmierung 7
R LWJGL: Performance glBegin, drawList, ... Spiele- und Multimedia-Programmierung 16
K Android: OpenGL render performance Tipps ? Spiele- und Multimedia-Programmierung 4
B Performance Spiele- und Multimedia-Programmierung 19
Grejak Performance Spiele- und Multimedia-Programmierung 5
M Performance von BufferedImages Spiele- und Multimedia-Programmierung 15
A jogl 2d performance Spiele- und Multimedia-Programmierung 20
S Graphics2D Oval vs. Rect -Performance Spiele- und Multimedia-Programmierung 17
Taschenschieber Schachbrett mit SVG/Batik: Performance? Spiele- und Multimedia-Programmierung 3
Tapsi Anfänger braucht einen Rat ^^ --> Performance Spiele- und Multimedia-Programmierung 7
T performance Spiele- und Multimedia-Programmierung 10
egrath Vista OpenGL Performance Problem? Spiele- und Multimedia-Programmierung 3
I RPG-Spiel und Performance Spiele- und Multimedia-Programmierung 33
N Performance Problem bei mit Graphics Spiele- und Multimedia-Programmierung 6
M Spiel Performance erhöhen Spiele- und Multimedia-Programmierung 24
M Performance Spiele- und Multimedia-Programmierung 5
S [Java2D] Performance Frage Spiele- und Multimedia-Programmierung 4
H Performance check Pong Spiele- und Multimedia-Programmierung 19
M Space PingPong Game --> Performance ok? Spiele- und Multimedia-Programmierung 44
J Performance Spiele- und Multimedia-Programmierung 6
P Java3D Performance und Abstürze Spiele- und Multimedia-Programmierung 3
K Performance von J3D Spiele- und Multimedia-Programmierung 5

Ähnliche Java Themen

Neue Themen


Oben