Graphics2D Oval vs. Rect -Performance

spyboot

Bekanntes Mitglied
Hallo Community!

Ich habe eine Java-Anwendung die mehrere tausend mal pro Sekunde Graphics2D.fillOval(x,y,width,height) aufruft.
Leider braucht diese Methode sehr viel CPU-Zeit. Im Vergleich zu fillRect(x,y,width,height) ist sie vielleicht 20x langsamer.
Ich denke es liegt zum Teil daran dass die Methode ja jeden Punkt im Umfang des Kreises einzeln berechnen muss. Da aber jeder Kreis gleichgroß ist und nur in seiner Farbe variiert kann man dort vielleicht etwas machen dachte ich... leider wäre es zu speicherintensiv (10-20 mb) jede einzelne benötigte Variation der Farbe in einem BufferedImage abzulegen (es variieren rot und Transparenz, Grün und Rot sind 0 oder 100 . Transparenz wurde beim Speicherverbrauch nicht mitberechnet da man das BufferedImage transparent zeichnen kann) Gibt es vielleicht eine Möglichkeit vorher den Kreis berechnen zu lassen und hinterher die Kopien nur in einer anderen Farbe zu zeichnen?

Ich bin für alle Vorschläge dankbar

mfg. Spyboot
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Hm... da gäbe es verschiedene mögliche Ansätze. Im voraus zu sagen, welcher der effizienteste oder geeignetste ist, ist schwierig. Wie groß sind denn die Ovale (width/height)?

Ein Ansatz ... hm... klingt erstmal komisch, aber könnte effizient sein: Man malt einmal das Oval mit rot in ein BufferedImage. Dann geht man das BufferedImage einmal durch, und speichert sich die Koordinaten aller roten Pixel in einem int[][] oder einen Point[] array. Wenn man dann ein eingefärbtes Oval zeichnen will, könnte man für alle diese Punkte im BufferedImage setRGB(x,y,rgb) machen und das Bild dann zeichnen... aber... mal überlegen, ob es eine ... elegantere Möglichkeit gibt... (Erst dachte ich, man müßte das mit einem geeigneten Composite und/oder Paint machen können, aber WIE genau wüßte ich spontan nicht... ???:L )
 

spyboot

Bekanntes Mitglied
Hmm eleganter: eine Methode die die Punkte eines Kreises durchgeht habe ich bereits (war damals für eine Art Strichmann-Worms). Ich suche sie raus, mache einen Performance-Test und poste sie mal...
 

Steev

Bekanntes Mitglied
Und wieso nimmst du nicht einfach für die nötigsten Sachen deines Spieles Bitmaps? Bitmaps werden etliche male schneller gerendert als eine vergleichbare Form mithilfe einer Graphics-Zeichenmethoden...
 

icarus2

Top Contributor
Ich nehme einmal an, dass das ganze in Zusammenhang mit einem Spiel gezeichnet werden soll? Also alles in Echtzeit geschieht?

Du hast ja gesagt, dass du mehrere tausen mal in der Sekunde fillRect(...) aufrufst, das ja wohl immer mit einem repaint() gekoppelt ist. Ein normaler Rechnerbildschirm hat meines Wissens so ca. 60Hz, was heisst, dass das Bild in der Sekunde 60 mal gezeichnet wird.

Sind so viele einzelne fillRect(...)-Aufrufe also wirklich notwendig? Wäre es nicht einfacher grössere "Rechensprünge" zu machen? Weil es bringt ja nichts den Bildschirm 1000 mal in der Sekunde zu zeichnen, wenn das Bild onehin nur ca. 60 mal aktualisiert wird.

Thread.sleep(long) wäre da wohl angebracht denke ich.

*Edit
Es gibt auch Bildschirme mit 100Hz habe ich gerade irgendwo im Internet gesehen. Aber auch schon bei 60Hz hat der Mensch das Gefühl es sei eine flüssige Bewegung.

*Edit 2:
Oder liegt das Problem darin, dass du pro repaint() sehr viele fillRect(...) machen musst?
 
Zuletzt bearbeitet:

Steev

Bekanntes Mitglied
Ich nehme mal an, das er bei seinem Stickman-Worms ziemlich viele Strichmännchen hat, die er pro Sekunde neu zeichnen muss.
Wenn er dann noch die Levelgeometrie über Graphics-Zeichenmethoden zeichnet, dann kommen da schon einige drawOvals usw. zusammen.
Da ich das vermute, währe es meiner Meinung nach Sinnvolle wenigstens die Charaktere mittels BufferedImage in ein VolatileImage zu zeichenen, weil das nunmal um einiges schneller ist. Was er mit seinen Farbkombinationen machen will, kapiere ich jetzt nicht ganz...
 
G

Gelöschtes Mitglied 9001

Gast
Wie wäre es, das Oval aufzuteilen in mehrere Formen: 1 großes Rechteck in der Mitte und 4 Ränder, die die Bogensegmente darstellen. Da oben und unten sowie links und rechts jeweils nur gespiegelt werden müssen, müssen nur 2 Bilder im Speicher vorgehalten werden.
Für das Rechteck dann fillRect nehmen und die Bogensegmente als Bilder darstellen.
 

Steev

Bekanntes Mitglied
Wo wir schon bei BufferedImage sind sollte VolatileImage nicht unerwähnt bleiben :p
Da wird dann direkt über die Grafikkarte gezeichnet, was noch schneller ist. Formen, wie Strichmännchen usw. werden in BufferedImages abgespeichert, da VolatileImages flüchtig sind.
 

spyboot

Bekanntes Mitglied
Leider brachte VolatileImage gar keinen Performance-Unterschied und die Idee mit setRGB(x,y,rgb) brachte neben extremen Performance-Einbrüchen noch unschöne Graffikfehler mit (wenn zwei kreise sich überlappen die eigentlich transparent sind).
Dass Ganze sollte eine Art Feuer werden. Was leider wenig Sinn hat wenn dass Spiel bei circa 300 Partikeln (aus denen sich die Feuer zusammensetzen) welche als Ovale gezeichnet werden 100% CPU last bringt (3Ghz) (300 Partikel entsprechen ca. 5 Feuern). Dann bliebe echt keine Leistung mehr für den Rest. Ich werde nun wohl auf eine weniger aufwändige Feueranimation setzen müssen aber ich danke euch trotzdem für euren guten Rat.

mfg. Spyboot
 

Steev

Bekanntes Mitglied
Probiere einmal aus anstatt Transparente Ovale zu zeichnen Bitmaps zu verwenden und diese zu skalieren. Für Bitmap-Transparenz solltest du AlphaComposites verwenden.
Mit diesem Ansatz sollte das ganze merklich schneller werden.

PS: Die optimale Anzahl von Partikeln liegt in der Regel bei 80-150. Einen besseren Effekt bekommt man, wenn man keine Ovale sondern Vielecke als Bitmaps verwendet und diese dann zufallsbasiert rotiert.

Gruß
Steev
 
Zuletzt bearbeitet:

spyboot

Bekanntes Mitglied
Die Bitmaps sind eine tolle Idee hatte dass weiter oben wahrscheinlich einfach überlesen.
Nun ja, dann Code ich das mal direkt mache einen Performance-Test und Schreibe wie es gelaufen ist.
 

spyboot

Bekanntes Mitglied
Mmmmh dass mit den Bitmaps geht zwar, bringt aber leider auch den erwarteten Speicherverbrauch mit sich. Ich konnte noch ein bisschen weg optimieren weil ich ein paar fälle noch ausschließen konnte. Der Speicherverbrauch liegt jetzt bei 6 MB. Wenigstens habe ich jetzt eine performante Lösung gefunden.

Danke.
 

Steev

Bekanntes Mitglied
Bei 6 MB Speicherverbrauch kann man an sich nicht meckern. Auch wenn mir nicht in den Kopf will, wie du mit eins zwei Grafiken die warscheinlich maximal eine Größe von 25x25x32 haben 6MB verbrauchen kannst :-D

Gibt es schon ScreenShots oder so etwas, mich interessiert dein Stickman-Worms, lustige Idee :)
 

spyboot

Bekanntes Mitglied
Hmm also das Stickmann-Worms war eines meiner ersten Java-Projekte. Damals war ich 12/13 und dementsprechend ist es auch Programmiert. Wenn ich den kompletten Source wieder zusammenkriege Poste ich es mal: Es ist leider ein Applet und funktioniert außerhalb der Eclipse-Umgebung nicht. Woran ich arbeite ist kein Stickmann-Worms sondern eine Art äh... Wie Castle-Defense ohne Castles. Weiß nicht... am besten ihr seht euch dass Ergebnis an. Ich hatte nur den Algorythmus zum durchgehen aller Punkte in einem Kreis geschrieben um Ovale aus der Landschaft zu sprengen.
 
Zuletzt bearbeitet:

Steev

Bekanntes Mitglied
Und wieso arbeitest du nicht mit Maskierungen der Landschaft? Mithilfe einer Alphamaske müsste man doch ohne größere Probleme einen Krater in die Landschaft sprengen können :)
 

spyboot

Bekanntes Mitglied
Wie gesagt damals war ich Anfänger und habe für Kollisionen eine boolean[][]-map Verwendet. Naja jedenfalls hatte ich daher halt noch eine Methode dafür. Ist jetzt ja auch egal da wir es eh anders gelöst haben.
 

Neue Themen


Oben