8 Stimmen

Schreckliche Canvas GetImageData() / PutImageData() Leistung auf dem Handy

Ich tue ein kleines HTML5-Spiel und während des Ladens meiner Sprites am Anfang der Karte, tue ich einige Verarbeitung mit GetImageData() / Schleife über alle das Bild / PutImageData().

Auf meinem PC funktioniert das fantastisch, aber auf meinen Handys ist es furchtbar langsam.

PC: 5-6 ms
iPhone 4: 300-600 ms
Android HTC Desire S: 2500-3000 ms

Ich habe einige SEHR grundlegende Benchmarking, und beide GetImageData und PutImageData laufen sehr schnell, was lange dauert, ist die Schleife durch den Inhalt.

Nun, ich natürlich erwarten eine Verlangsamung auf dem Telefon, aber 1000x klingt ein bisschen übertrieben, und das Laden dauert etwa 4 Minuten auf meinem HTC, so dass das nicht funktionieren wird. Auch alles andere im Spiel funktioniert mit sehr angemessener Geschwindigkeit (vor allem, weil der Bildschirm lächerlich kleiner ist, aber immer noch, es funktioniert überraschend gut für JS auf einem Handy)


Was ich in dieser Verarbeitung tue, ist im Grunde "Verdunkelung" das Sprite zu einem bestimmten Grad. Ich einfach Schleife durch alle Pixel, und multiplizieren sie mit einem Wert < 1. Das ist alles.

Da dies zu langsam ist... Gibt es einen besseren Weg, dasselbe zu tun, mit der Canvas-Funktionalität, (Compositing, Deckkraft, was auch immer), ohne Schleife durch alle Pixel einer nach dem anderen?

HINWEIS: Diese Ebene hat einige 100% transparente Pixel und einige 100% undurchsichtige Pixel. Beide müssen entweder 100 % undurchsichtig oder 100 % transparent bleiben.

Ich habe mir Dinge ausgedacht, die nicht funktionieren würden:
1) Malen der Sprites in einer neuen Leinwand, mit geringerer Deckkraft. Dies wird nicht funktionieren, weil ich die Sprites zu bleiben undurchsichtig, nur dunkler brauchen.
2) Malen der Sprites und ein halbtransparentes schwarzes Rechteck über sie malen. Dies wird sie dunkler machen, aber es wird auch meine transparenten Pixel nicht mehr transparent machen...

Irgendwelche Ideen?

Dies ist der Code, den ich verwende, nur für den Fall, dass Sie darin etwas furchtbar Idiotisches sehen:

function DarkenCanvas(baseImage, ratio) {
    var tmpCanvas = document.createElement("canvas");
    tmpCanvas.width = baseImage.width;
    tmpCanvas.height = baseImage.height;
    var ctx = tmpCanvas.getContext("2d");
    ctx.drawImage(baseImage, 0, 0);

    var pixelData = ctx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
    var length = pixelData.data.length;
    for (var i = 0; i < length; i+= 4) {
        pixelData.data[i] = pixelData.data[i] * ratio;
        pixelData.data[i + 1] = pixelData.data[i + 1] * ratio;
        pixelData.data[i + 2] = pixelData.data[i + 2] * ratio;
    }

    ctx.putImageData(pixelData, 0, 0);
    return tmpCanvas
}

EDIT : Dies ist ein Beispiel für das, was ich mit dem Bild zu tun versuche:
Original: http://www.crystalgears.com/isoengine/sprites-ground.png
Verdunkelt: http://www.crystalgears.com/isoengine/sprites-ground_darkened.png

Danke!
Daniel

12voto

Simon Sarris Punkte 60248

Phrogz hat die richtige Idee. Sie wollen wirklich nur eine halb-transparent (oder Verhältnis-transparent) schwarz über die ganze Sache mit 'source-atop' globalCompositeOperation zu malen.

Zum Beispiel so: http://jsfiddle.net/F4cNg/

Es gab eigentlich eine gute Frage zu einer ausgeklügelten Verdunkelung wie dieser, aber der Autor hat sie gelöscht, was wirklich schade ist. Es ist sicherlich möglich, dunkle Masken auf gezeichnetem Material zu erstellen, sogar mit ausgefeilten Formen. Sie helfen Ihnen vielleicht nicht weiter, aber der Vollständigkeit halber und für alle, die nach "Leinwand verdunkeln"-Zeug suchen, bei dem einige Teile hell bleiben (Ausschlusszonen), kann das mit der 'xor' globalCompositeOperation gemacht werden, etwa so:

http://jsfiddle.net/k6Xwy/1/

11voto

Jacquie Punkte 111

Kennen Sie diesen Tipp zur Leistung? http://ajaxian.com/archives/canvas-image-data-optimization-tip

Sie sprechen über die Reduzierung der Aufrufe an das DOM, um die Leistung zu steigern.

Mit diesem Tipp können Sie es also versuchen:

var pixel = ctx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
    pixelData=pixel.data; // detach the pixel array from DOM
    var length = pixelData.length;
    for (var i = 0; i < length; i+= 4) {
        pixelData[i] = pixelData[i] * ratio;
        pixelData[i + 1] = pixelData[i + 1] * ratio;
        pixelData[i + 2] = pixelData[i + 2] * ratio;
    }

Ihre .data-Anrufe können Sie ausbremsen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X