Почему установка значений CanvasPixelArray в HTML5 смехотворно медленная и как я могу сделать это быстрее? - PullRequest
21 голосов
/ 04 апреля 2010

Я пытаюсь создать некоторые динамические визуальные эффекты, используя пиксельные манипуляции холста HTML 5, но я сталкиваюсь с проблемой, когда установка пикселей в 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.

  1. Это предположение верно?
  2. Есть ли способ уменьшить время, затрачиваемое на установку значений в CanvasPixelArray?

Ответы [ 4 ]

12 голосов
/ 02 июня 2010

Попробуйте кэшировать ссылку на массив пикселей 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);
4 голосов
/ 26 ноября 2010

Не знаю, поможет ли это вам, потому что вы хотите манипулировать пикселями, но для меня в 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)
1 голос
/ 17 сентября 2015

Как ни странно, циклы через массивы 2d объектов выполняются быстрее, чем вычисления смещения 1d массива и отсутствуют объекты. Отформатируйте соответственно и посмотрите, поможет ли это (в моих тестах это было в 20 раз быстрее).

(заголовок: этот скрипт может вызвать сбой в вашем браузере! Если вы запустите его, сидите несколько минут и дайте ему сделать свое дело) 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

1 голос
/ 04 апреля 2010

Похоже, что вы делаете какое-то "блинтование", поэтому, возможно, drawImage или все сразу putImageData может помочь. Цикл четверти миллиона раз для отдельного копирования пикселей, вместо того, чтобы использовать масштабные операции «блицания», имеет тенденцию быть намного медленнее - и не только в Javascript; -).

...