Почему putImageData такой медленный? - PullRequest
36 голосов
/ 17 октября 2010

Я работаю с относительно большим Canvas, к которому обращаются различные (сложные) вещи. Затем я хочу сохранить состояние Canvas, чтобы быстро восстановить его до состояния, в котором оно находится сейчас. Я использую getImageData для этого и храню данные в переменной. Затем я рисую еще кое-что на холст и позже верну Canvas к тому месту, где он был при сохранении его состояния, используя putImageData.

Однако оказывается, что putImageData очень медленный. Фактически, это медленнее, чем просто перерисовка всего Canvas с нуля, что включает в себя несколько drawImage, покрывающих большую часть поверхности, и более 40 000 операций lineTo, сопровождаемых штрихами и заливками.

Перерисовка холста размером примерно 2000 x 5000 пикселей с нуля занимает ~ 170 мс, с использованием putImageData, хотя и занимает колоссальные 240 мс. Почему putImageData такой медленный по сравнению с перерисовкой холста, хотя перерисовка холста включает заполнение почти всего холста с помощью drawImage, а затем снова заполнение примерно 50% холста полигонами, используя lineTo, обводку и заливку. Таким образом, при перерисовке каждый пиксель затрагивается хотя бы один раз.

Поскольку drawImage выглядит намного быстрее, чем putImageData (в конце концов, часть drawImage при перерисовке холста занимает менее 30 мс). Я решил попытаться сохранить состояние холста, не используя getImageData, а вместо этого используя canvas.toDataURL, а затем создав изображение из URL-адреса данных, который я вставил в drawImage, чтобы нарисовать его на холсте. Оказывается, вся эта процедура намного быстрее и занимает всего 35 мсек.

Так почему же putImageData намного медленнее, чем альтернативы (с использованием getDataURL или просто перерисовкой)? Как я мог ускорить процесс дальше? Есть ли и если, вообще, лучший способ сохранить состояние холста?

(Все числа измеряются с помощью Firebug из Firefox)

Ответы [ 3 ]

77 голосов
/ 14 мая 2011

Просто небольшое обновление о том, что лучший способ сделать это.На самом деле я написал свою дипломную работу на тему High Performance ECMAScript и HTML5 Canvas (pdf, немецкий), поэтому я уже накопил некоторые знания по этой теме.Очевидно, что лучшим решением является использование нескольких элементов canvas.Рисование с одного холста на другой холст происходит так же быстро, как рисование произвольного изображения на холсте.Таким образом, «сохранение» состояния холста происходит так же быстро, как и восстановление его позже при использовании двух элементов canvas.

Этот тестовый пример jsPerf очень ясно показывает различные подходы, их преимущества и недостатки..

Просто для полноты, вот как вы действительно должны сделать это:

// setup
var buffer = document.createElement('canvas');
buffer.width = canvas.width;
buffer.height = canvas.height;


// save
buffer.getContext('2d').drawImage(canvas, 0, 0);

// restore
canvas.getContext('2d').drawImage(buffer, 0, 0);

Это решение, в зависимости от браузера, в 5000 раз быстрее, чем одноthe upvotes.

11 голосов
/ 26 ноября 2010

В Firefox 3.6.8 я смог обойти медлительность putImageData, используя вместо этого toDataUrl / drawImage.Для меня это работает достаточно быстро, и я могу вызвать его при обработке события mousemove:

Для сохранения:

savedImage = new Image()
savedImage.src = canvas.toDataURL("image/png")

Для восстановления:

ctx = canvas.getContext('2d')
ctx.drawImage(savedImage,0,0)
2 голосов
/ 17 октября 2010

Сначала вы говорите, что измеряете с помощью Firebug. На самом деле я обнаружил, что Firebug значительно замедляет выполнение JS, так что вы, возможно, не получите хорошие показатели за производительность.

Что касается putImageData, я подозреваю, что это связано с тем, что функции принимают большой массив JS, содержащий множество Number объектов, каждый из которых должен быть проверен на диапазон (0..255) и скопирован в собственный буфер canvas.

Может быть, когда станут доступны типы WebGL ByteArray, такие вещи можно будет сделать быстрее.

Кажется странным, что декодирование и распаковка данных base64 (с URL-адресом данных PNG) происходит быстрее, но при этом вызывается только одна функция JS с одной строкой JS, поэтому она использует в основном собственный код и типы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...