# Graphics2D  Oval vs. Rect  -Performance



## spyboot (21. Feb 2010)

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


----------



## Marco13 (21. Feb 2010)

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 (21. Feb 2010)

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 (21. Feb 2010)

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...


----------



## Marco13 (21. Feb 2010)

Das habe ich jetzt mal wegen...



spyboot hat gesagt.:


> leider wäre es zu speicherintensiv (10-20 mb) jede einzelne benötigte Variation der Farbe in einem BufferedImage abzulegen



... außen vor gelassen. Einmal so eine Art "template-Bild" anzulegen, und das vor jedem Zeichnen anzupassen wäre so der offensichtlichste Zwischenschritt....


----------



## icarus2 (21. Feb 2010)

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?


----------



## Steev (22. Feb 2010)

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...


----------



## Gelöschtes Mitglied 9001 (22. Feb 2010)

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.


----------



## Empire Phoenix (23. Feb 2010)

Das ganze spiel als ein Bufferdimage und nur änderungen neu berechnen? Soltle die Anzalh der benötigten aufrufe drastisch reduzieren.


----------



## Steev (23. Feb 2010)

Wo wir schon bei BufferedImage sind sollte VolatileImage nicht unerwähnt bleiben 
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 (24. Feb 2010)

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 (24. Feb 2010)

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


----------



## spyboot (25. Feb 2010)

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 (25. Feb 2010)

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 (25. Feb 2010)

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 (26. Feb 2010)

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.


----------



## Steev (26. Feb 2010)

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 (27. Feb 2010)

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.


----------

