Ich arbeite mit einem relativ großen Canvas, auf dem verschiedene (komplexe) Dinge gezeichnet werden. Dann möchte ich den Zustand des Canvas speichern, damit ich ihn später schnell wieder in den aktuellen Zustand zurücksetzen kann. Dazu verwende ich getImageData und speichere die Daten in einer Variablen. Dann zeichne ich noch etwas auf die Leinwand und setze die Leinwand später mit putImageData wieder in den Zustand zurück, in dem sie sich beim Speichern befand.
Es hat sich jedoch herausgestellt, dass putImageData sehr langsam ist. Es ist sogar langsamer als das Neuzeichnen des gesamten Canvas von Grund auf, was mehrere drawImage-Operationen erfordert, die den größten Teil der Oberfläche abdecken, und über 40.000 lineTo-Operationen, gefolgt von Strichen und Füllungen.
Das Neuzeichnen der ca. 2000 x 5000 Pixel großen Leinwand von Grund auf dauert ca. 170 ms, die Verwendung von putImageData dauert jedoch satte 240 ms. Warum ist putImageData so langsam im Vergleich zum Neuzeichnen des Canvas, obwohl das Neuzeichnen des Canvas das Füllen fast des gesamten Canvas mit drawImage und dann wieder das Füllen von etwa 50% des Canvas mit Polygonen mit lineTo, stroke und fill beinhaltet. Im Grunde wird also jedes einzelne Pixel beim Neuzeichnen mindestens einmal berührt.
Weil drawImage so viel schneller zu sein scheint als putImageData (immerhin dauert der drawImage-Teil des Neuzeichnens der Leinwand weniger als 30 ms). Ich beschloss zu versuchen, den Zustand der Leinwand nicht mit getImageData, sondern mit canvas.toDataURL zu speichern und dann ein Bild aus der Daten-URL zu erstellen, das ich in drawImage einfügen würde, um es auf die Leinwand zu zeichnen. Es stellte sich heraus, dass dieser ganze Vorgang viel schneller ist und nur etwa 35 ms dauert.
Warum ist also putImageData so viel langsamer als die Alternativen (Verwendung von getDataURL oder einfaches Neuzeichnen)? Wie könnte ich die Dinge weiter beschleunigen? Gibt es und wenn, was ist im Allgemeinen der beste Weg, um den Zustand einer Leinwand zu speichern?
(Alle Zahlen wurden mit Firebug von Firefox aus gemessen)
1 Stimmen
Es wäre interessant, wenn Sie irgendwo eine Demonstration Ihres Problems online stellen könnten. In noVNC ( github.com/kanaka/noVNC ) Ich verwende putImageData für viele kleine und mittelgroße Bilddaten-Arrays und ich sehe kein Leistungsproblem mit putImageData. Vielleicht haben Sie ein spezifisches Leistungsproblem, das behoben werden sollte.
0 Stimmen
Hier können Sie es sich ansehen danielbaulig.de/A3O Es wird nicht 100% funktionieren, wenn die Firebug-Konsole ausgeschaltet ist, also stellen Sie sicher, dass Sie sie einschalten. Die geprüfte Version ist diejenige, die putImageData verwendet. Sie können es auslösen, indem Sie auf eine beliebige "Kachel" klicken. Der Puffer-Canvas wird mit putImageData aktualisiert und dann die ausgewählte Kachel "hervorgehoben". In a3o_oo.js sind einige Zeilen auskommentiert, die verwendet werden können, um zwischen der Verwendung von putImageData (current), der Verwendung von getDataURL (die beiden Zeilen, die this.boardBuffer erwähnen) und dem einfachen Neuzeichnen (die drawBoard-Zeile) der Pufferfläche zu wechseln.
0 Stimmen
Tolle Frage und tolle Lösungen. Aber haben Sie jemals den wahren Grund herausgefunden, warum putImageData so langsam ist im Vergleich zu drawImage?
1 Stimmen
@cherouvim Nein, eigentlich nicht. Ich gehe davon aus, dass der Hauptgrund dafür ist, dass die ImageData-Struktur nicht mehr in hardwarebeschleunigten Grafikstrukturen verwaltet wird und somit der Aufruf von getImageData/putImageData zu/von diesen Objekten übersetzt werden muss, was langsam ist, das Kopieren vieler Daten, das Instanziieren von Objekten usw. beinhaltet, während die Verwendung von drawImage einfach eine bereits vorhandene Textur/einen Zeichenkontext auf den Bildschirm zeichnet - was mit moderner Hardware unglaublich schnell ist.