Ужасная производительность Canvas GetImageData () / PutImageData () на мобильном устройстве - PullRequest
8 голосов
/ 10 декабря 2011

Я играю в небольшую HTML5-игру и, загружая свои спрайты в начале карты, я выполняю некоторую обработку с помощью GetImageData () /, перебирая все изображение / PutImageData ().

Это потрясающе работает на моем ПК, однако на мобильных телефонах это ужасно медленно.

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

Я проводил ОЧЕНЬ базовый бенчмаркинг, и GetImageData, и PutImageData работают очень быстро, что занимает много времени, это циклический просмотр содержимого.

Теперь я , очевидно, ожидаю замедления на телефоне, но 1000х звучит немного чрезмерно, и загрузка на моем HTC занимает около 4 минут, так что это не сработает. Кроме того, все остальное в игре работает на очень разумной скорости (главным образом потому, что экран смехотворно меньше, но все же он работает на JS на мобильном телефоне на удивление хорошо)


То, что я делаю в этой обработке - это "затемнение" спрайта до определенного уровня. Я просто перебираю все пиксели и умножаю их на значение <1. Вот и все. </p>

Так как это слишком медленно ... Есть ли лучший способ сделать то же самое, используя функциональность Canvas (композитинг, прозрачность и т. Д.), Без циклического прохождения всех пикселей по одному?

ПРИМЕЧАНИЕ. Этот слой имеет около 100% прозрачных пикселей и около 100% непрозрачных пикселей. Оба должны оставаться либо на 100% непрозрачными, либо на 100% прозрачными.

Вещи, о которых я думал, не сработают:
1) Рисование спрайтов на новом холсте с меньшей непрозрачностью. Это не сработает, потому что мне нужно, чтобы спрайты оставались непрозрачными, только темнее.
2) Рисование спрайтов и рисование полупрозрачного черного прямоугольника поверх них. Это сделает их темнее, но мои прозрачные пиксели больше не будут прозрачными ...

Есть идеи?

Это код, который я использую, на случай, если вы увидите в нем что-то ужасно идиотское:

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
}

РЕДАКТИРОВАТЬ: Это пример того, что я пытаюсь сделать с изображением:
Оригинал: http://www.crystalgears.com/isoengine/sprites-ground.png
Потемнело: http://www.crystalgears.com/isoengine/sprites-ground_darkened.png

Спасибо!
Daniel

Ответы [ 2 ]

11 голосов
/ 10 декабря 2011

Phrogz имеет правильную идею. Вы действительно просто хотите закрасить полупрозрачный (или прозрачный с точки зрения пропорций) черный цвет по всему объекту с помощью 'source-atop' globalCompositeOperation.

Как это: http://jsfiddle.net/F4cNg/

На самом деле был хороший вопрос о таком сложном затемнении, но автор удалил его, что является настоящим позором. Безусловно, можно создавать темные маски на нарисованных предметах, даже со сложными формами. Они могут вам не помочь, но ради полноты и для любого, кто ищет материал «затемненного холста», где некоторые части остаются светлыми (зоны отчуждения), что можно сделать с помощью глобальной операции xor «xor», например:

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

10 голосов
/ 06 октября 2012

Вы видели этот совет по производительности ?: http://ajaxian.com/archives/canvas-image-data-optimization-tip

Они говорят о сокращении вызовов в DOM для повышения производительности.

Итак, на основе этого совета вы можете попробовать:

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;
    }

Ваши вызовы .data могут замедлять вас.

...