# Graphics.drawImage() sehr schnell



## Kento (9. Apr 2010)

Hallo Community,

ich arbeite mit Java 6 unter Windows 7 und habe ein paar Fragen bezüglich der drawImage() Methode von der java.awt.Graphics Klasse. Denn mit
dieser Methode kann man ein Bild oder einen Bildausschnitt zeichnen. 

Ich machte mir zuerst sorgen wegen der Performance, denn mit anderen Technologien wie .NET habe ich
festgestellt, dass wenn viel gezeichnet wird die Anwendung in die Knie geht. Doch nun musste ich überraschend feststellen, das dem scheinbar nicht so ist.

Ich habe mir dazu ein Beispielprogramm gebaut, dieses besteht aus einem JFrame, in welchem eine
JScrollPane eingesetzt wurde. In der JScrollPane steckt eine eigene, von JComponent abgeleitete
Komponente, welche eine Tilemap zeichnet. Wobei jedes Tile eine Größe von 32x32 Pixel besitzt. Diese eigene Komponente habe ich MapPanel genannt.

Die Größe dieser Komponente wurde auf 3200x3200 Pixel gesetzt, damit diese eine 100x100 Tilemap darstellen kann. Es wird aber natürlich immer nur der sichtbare Bereich der Tilemap gezeichnet.

Der Zeichenvorgang, darum geht es mir nämlich, wird in einer 3-Fach geschachtelten For-Schleife durchgeführt:


```
for(int x = 0; x < w; x++)
{
    for(int y = 0; y < h; y++)
    {
        for(int z = 0; z < 5; z++)
        {
            t = this.mapData[tx + x][ty + y][z];
            
            // Wenn 0 wird Gras gezeichnet, ansonsten eine gelbe Pflanze
            if(t == 0)
            {
                g.drawImage(this.tileset, scrX + x * 32,
                        scrY + y * 32,
                        scrX + x * 32 + 32,
                        scrY + y * 32 + 32, 0, 0, 32, 32, null);
            }
            else
            {
                g.drawImage(this.tileset, scrX + x * 32,
                scrY + y * 32,
                scrX + x * 32 + 32,
                scrY + y * 32 + 32, 32, 0, 64, 32, null);
            }
        }
    }
}
```

Die Variablen w und h in den Schleifen-Köpfen der beiden äußeren Schleifen speichern die Größe des sichtbaren Bereichs(in Tiles).

Die If-Abfrage innerhalb der Schleife dient dazu, je nach Lage der Map-Daten entweder Gras oder eine gelbe Pflanze zu zeichnen. Die Variablen tx und ty werden zu x und y addiert, da sie den Scroll-Wert(in Tiles) der Scroll-Balken beinhalten. Die Variablen scrX und scrY sind ähnlich wie tx und ty, nur speichern sie den Scroll-Wert in Pixeln und wurden zusätzlich modifiziert, so das ein weiches Scrolling entsteht.

Aber worum es eigentlich geht, die Schleife zeichnet eine Tilemap mit 5 Ebenen. Absolut problemlos,
mit Scrolling und allem drum und dran. Auch Tiles lassen sich per Mausklick verändern, ich habe schon einiges probiert und es geht problemlos. Und ich verstehe nicht warum es absolut KEIN bisschen ruckelt beim scrollen.

Selbst wenn ich 25 Ebenen zeichne ruckelt es nicht. Selbst dann, wenn ich die höchste Auflösung die mein Rechner hergibt einstelle(1280x1024) und das Fenster maximiere. Erst ab 50 Ebenen fängt es dann an zu stocken.
Das erscheint mir irgendwie zu krass, könnt ihr mir das erklären? Ich meine ist ja toll dass das sogut
läuft, aber ich kann das einfach nicht glauben. Das muss doch eine Täuschung sein.

gruß

Kento


----------



## Marco13 (9. Apr 2010)

Na DAS ist doch mal eine Problembeschreibung, die ich auf der Arbeit gerne hören würde 

Warum genau es wie schnell im Vergleich zu welcher anderen Methode ist, ist schwer zu sagen. Aber - sowas wie drawImage malt natürlich nur, wenn der Bereich in den gezeichnet wird auch sichtbar ist. Außerdem verwendet Java (je nach Version und Startparametern) OpenGL oder DirectX als Beschleunigung, und wenn man nicht durch irgendwelche zuuu strengen Vorgaben etwas tut, was dem im Wege steht, werden Bilder in ihrer "optimalen" Form erstellt - also so, dass sie schnell und hardwarebeschleunigt gezeichnet werden können. (Ein nicht ganz unüblicher (aber doch noch zu selten auftretender) Effekt: Wenn etwas als problematisch dargestellt oder kritisiert wird, wird SO massiv etwas dagegen unternommen, dass ... dann solche Beiträge wie deiner rauskommen - und wir wissen ja alle: "Java is voll lahm" - "Joa, und Swing erst, das is nichma nativ!"  )

Bei diesem speziellen Fall (dem Scrollen) könnte(!) aber ein Punkt der entscheidende sein: Eine JScrollPane hat intern einen JViewport, und der verwendet clevere Tricks, um das Scrollen schnell zu machen - da kann man sich mal Methoden wie JViewport (Java 2 Platform SE 5.0) ansehen. Vielleicht schaffst du es ja, mit einem anderen ScrollMode das ganze langsamer zu machen  
( :joke: )


----------



## SlaterB (9. Apr 2010)

und beim Scrollen sollte doch die paint-Methode auch nur einmal durchlaufen werden und ein größeres Stück malen dass dann verschoben wird, oder? 
wäre leicht durch Log-Ausgaben zu testen,
(edit: ok, das steht bei BACKINGSTORE_SCROLL_MODE ja auch)

ein fertiges Bild zu verschieben ist nicht so schwer, bei evtl. noch einem Mausklick pro Sekunde neuzuzeichnen wäre dann auch nicht die Kunst


----------



## André Uhres (9. Apr 2010)

Kento hat gesagt.:


> Und ich verstehe nicht warum es absolut KEIN bisschen ruckelt beim scrollen. [...] Das erscheint mir irgendwie zu krass, könnt ihr mir das erklären?


Die ruck- und flackerfreien Malvorgänge des Swing RepaintManagers werden einerseits durch die in Swing eingebaute Doppelpufferung ermöglicht und andererseits durch diverse JComponent Eigenschaften optimiert. Z.B. gibt die JComponent Methode "isValidateRoot()" für die meisten Komponenten "false" zurück. JScrollPane ist, neben JRootPane und JTextField, eine der wenigen Komponenten die true als default Wert zurück geben. Das bedeutet, dass der Inhalt eines JScrollPane in jeder Situation erfolgreich ausgelegt werden kann, ohne irgendwelche Vorfahren oder Geschwister zu berühren. Das ist wohl in diesem Fall ein wesentlicher Faktor für die Leistungsfähigkeit der Darstellung.


----------



## Marco13 (9. Apr 2010)

@André Uhres: Bist du sicher, dass das hier gemeint ist (und Einfluß darauf hat)? ???:L Wenn man eine ScrollPane mit einer riesen Component darin hat, dann dürfte da Double Buffering keinen Geschwindkeitsvorteil bringen - und layouten muss er beim Scrollen ohnehin nicht :bahnhof: Ich tippe eher darauf, dass da die ScrollPane mit ihrem Viewport das aufwändige Neuzeichnen "versteckt". Bemerken würde man das vermutlich spätestens, wenn man den ScollMode umschalten oder in dieser Component etwas animiertes zeichnen würde...


----------



## André Uhres (9. Apr 2010)

Marco13 hat gesagt.:


> @André Uhres: Bist du sicher, dass das hier gemeint ist (und Einfluß darauf hat)?


Ich wollte eigentlich auch nur darauf hinweisen, dass Swing über eine ganze Reihe von ausgefeilten und intelligenten Mechanismen verfügt, die alle dasselbe Ziel verfolgen, nämlich die Leistungfähigkeit der Darstellung zu maximieren. Was hier letztlich auschlaggebend ist, kann ich auch nur vermuten. Man kann ja nicht alles wissen  .


----------

