Ускорьте рендеринг пикселей в HTML5 Canvas
Я разрабатываю веб-приложение в стиле Photoshop, работающее на элементе HTML5 Canvas. Программа работает хорошо и очень быстро, пока я не добавлю режимы наложения в уравнение. Я достигаю режимов наложения, объединяя каждый элемент холста в один и комбинируя каждый пиксель с каждого холста, используя правильные режимы наложения, начиная с нижнего холста.
for (int i=0; i<width*height*4; i+=4) {
var base = [layer[0][i],layer[0][i+1],layer[0][i+2],layer[0][i+3]];
var nextLayerPixel = [layer[1][i],layer[1][i+1],layer[1][i+2],layer[1][i+3]];
//Apply first blend between first and second layer
basePixel = blend(base,nextLayerPixel);
for(int j=0;j+1 != layer.length;j++){
//Apply subsequent blends here to basePixel
nextLayerPixel = [layer[j+1][i],layer[j+1][i+1],layer[j+1][i+2],layer[j+1][i+3]];
basePixel = blend(basePixel,nextLayerPixel);
}
pixels[i] = base[0];
pixels[i+1] = base[1];
pixels[i+2] = base[2];
pixels[i+3] = base[3];
}
canvas.getContext('2d').putImageData(imgData,x,y);
При этом вызывая blend для разных режимов наложения. Мой "нормальный" режим смешивания выглядит следующим образом:
var blend = function(base,blend) {
var fgAlpha = blend[3]/255;
var bgAlpha = (1-blend[3]/255)*base[3]/255;
blend[0] = (blend[0]*fgAlpha+base[0]*bgAlpha);
blend[1] = (blend[1]*fgAlpha+base[1]*bgAlpha);
blend[2] = (blend[2]*fgAlpha+base[2]*bgAlpha);
blend[3] = ((blend[3]/255+base[3])-(blend[3]/255*base[3]))*255;
return blend;
}
Мои результаты испытаний в Chrome (с некоторыми из лучших из протестированных браузеров) составили около 400 мс, смешав три слоя вместе на холсте 620x385 (238 700 пикселей).
Это очень маленькая реализация, так как большинство проектов будет больше по размеру и будет включать в себя больше слоев, что значительно ускорит время выполнения этого метода.
Мне интересно, есть ли более быстрый способ объединить два контекста холста с режимом наложения без необходимости проходить через каждый пиксель.
2 ответа
Не создавайте так много массивов с 4 значениями, это должно идти намного быстрее при использовании существующей памяти. Кроме того, вы можете использовать reduce
функционировать на вашем layer
массив, это кажется именно то, что вам нужно. Тем не менее, использование каких-либо функций вообще может быть еще одним касанием быстрее - создание контекстов выполнения не требуется. Следующий код вызовет функцию смешивания только для каждого слоя, а не для каждого слоя пикселей *.
var layer = [...]; // an array of CanvasPixelArrays
var base = imgData.data; // the base CanvasPixelArray whose values will be changed
// if you don't have one, copy layer[0]
layer.reduce(blend, base); // returns the base, on which all layers are blended
canvas.getContext('2d').putImageData(imgData, x, y);
function blend(base, pixel) {
// blends the pixel array into the base array and returns base
for (int i=0; i<width*height*4; i+=4) {
var fgAlpha = pixel[i+3]/255,
bgAlpha = (1-pixel[i+3]/255)*fgAlpha;
base[i ] = (pixel[i ]*fgAlpha+base[i ]*bgAlpha);
base[i+1] = (pixel[i+1]*fgAlpha+base[i+1]*bgAlpha);
base[i+2] = (pixel[i+2]*fgAlpha+base[i+2]*bgAlpha);
base[i+3] = ((fgAlpha+base[i+3])-(fgAlpha*base[i+3]))*255;
// ^ this seems wrong, but I don't know how to fix it
}
return base;
}
Альтернативное решение: не смешивайте слои вместе в javascript. Просто разместите ваши полотна друг над другом и дайте им CSS opacity
, Это должно значительно ускорить отображение. Только я не уверен, будет ли это работать вместе с другими вашими эффектами, если они должны быть применены на нескольких слоях.
Традиционно массовые манипуляции с пикселями ускоряются за счет их запуска на GPU, а не на CPU. К сожалению, у canvas нет такой поддержки, но вы можете реализовать обходной путь, используя фильтры SVG. Это позволит вам использовать аппаратно ускоренные режимы наложения (feBlend), чтобы смешать два изображения вместе. Если вы визуализируете свои слои на два изображения, а затем ссылаетесь на эти изображения в SVG, вы можете сделать это.
Вот хороший иллюстрированный обзор того, как это может работать:
http://blogs.msdn.com/b/ie/archive/2011/10/14/svg-filter-effects-in-ie10.aspx (для IE10, но применяется к любому браузеру, который поддерживает фильтры SVG)