Почему значение HTML5 CanvasPixelArray смешно замедляется и как я могу сделать это быстрее?
Я пытаюсь сделать некоторые динамические визуальные эффекты, используя манипуляцию пикселов HTML 5 canvas, но у меня возникает проблема, когда установка пикселей в CanvasPixelArray смехотворно медленна.
Например, если у меня есть код вроде:
imageData = ctx.getImageData(0, 0, 500, 500);
for (var i = 0; i < imageData.length; i += 4){
imageData.data[i] = buffer[i];
imageData.data[i + 1] = buffer[i + 1];
imageData.data[i + 2] = buffer[i + 2];
}
ctx.putImageData(imageData, 0, 0);
Профилирование с помощью Chrome показывает, что он работает на 44% медленнее, чем следующий код, в котором CanvasPixelArray не используется.
tempArray = new Array(500 * 500 * 4);
imageData = ctx.getImageData(0, 0, 500, 500);
for (var i = 0; i < imageData.length; i += 4){
tempArray[i] = buffer[i];
tempArray[i + 1] = buffer[i + 1];
tempArray[i + 2] = buffer[i + 2];
}
ctx.putImageData(imageData, 0, 0);
Мое предположение заключается в том, что причина этого замедления связана с преобразованием между удвоением Javascript и внутренними целыми целыми 8-битными знаками, используемыми CanvasPixelArray.
- Правильно ли это предположение?
- Есть ли способ сократить время, затрачиваемое на установление значений в CanvasPixelArray?
Ответы
Ответ 1
Попробуйте кэшировать ссылку на массив пикселей data
. Ваше замедление может быть связано с дополнительными правами доступа к imageData.data
. Подробнее см. в этой статье.
например. Это должно быть быстрее, чем у вас в настоящее время.
var imageData = ctx.getImageData(0, 0, 500, 500),
data = imageData.data,
len = data.length;
for (var i = 0; i < len; i += 4){
data[i] = buffer[i];
data[i + 1] = buffer[i + 1];
data[i + 2] = buffer[i + 2];
}
ctx.putImageData(imageData, 0, 0);
Ответ 2
Я не знаю, поможет ли это вам, потому что вы хотите манипулировать пикселями, но для меня, в Firefox 3.6.8, просто вызов putImageData был очень, очень медленным, без каких-либо манипуляций с пикселями. В моем случае я просто хотел восстановить предыдущую версию изображения, которое было сохранено с помощью getImageData. Слишком медленно.
Вместо этого я получил его, чтобы хорошо работать, используя toDataUrl/drawImage. Для меня это работает достаточно быстро, чтобы я мог вызвать его при обработке события mousemove:
Чтобы сохранить:
savedImage = new Image()
savedImage.src = canvas.toDataURL("image/png")
Для восстановления:
ctx = canvas.getContext('2d')
ctx.drawImage(savedImage,0,0)
Ответ 3
Похоже, вы делаете что-то вроде "blitting", поэтому, возможно, drawImage или all-at-once putImageData может помочь. Цикл в полтора миллиона раз для копирования пикселей по отдельности, а не с использованием массивных "бьющих" операций, имеет тенденцию быть намного медленнее - и не только в Javascript; -).
Ответ 4
Как ни странно, петли через массивы объектов 2d быстрее, чем одномерные смещения массива и нет объектов. Соответствующий формат и посмотрите, помогает ли это (в моих тестах оно было в 20 раз быстрее).
(хэдз-ап: этот script может привести к краху вашего браузера! Если вы запустите его, сидите в течение нескольких минут и позволяйте ему делать это)
http://jsfiddle.net/hc52jx04/16/
function arrangeImageData (target) {
var imageCapture = target.context.getImageData(0, 0, target.width, target.height);
var imageData = {
data: []
};
imageData.data[0] = [];
var x = 0;
var y = 0;
var imageLimit = imageCapture.data.length;
for (var index = 0; index < imageLimit; index += 4) {
if (x == target.width) {
y++;
imageData.data[y] = [];
x = 0;
}
imageData.data[y][x] = {
red: imageCapture.data[index],
green: imageCapture.data[index + 1],
blue: imageCapture.data[index + 2],
alpha: imageCapture.data[index + 3]
};
x++;
}
return imageData;
}
function codifyImageData (target, data) {
var imageData = data.data;
var index = 0;
var codedImage = target.context.createImageData(target.width, target.height);
for (var y = 0; y < target.height; y++) {
for (var x = 0; x < target.width; x++) {
codedImage.data[index] = imageData[y][x].red;
index++;
codedImage.data[index] = imageData[y][x].green;
index++;
codedImage.data[index] = imageData[y][x].blue;
index++;
codedImage.data[index] = imageData[y][x].alpha;
index++;
}
}
return codedImage;
}
Дополнительная информация: http://discourse.wicg.io/t/why-a-straight-array-for-canvas-getimagedata/1020/6