我正在设计一个在HTML5 Canvas元素上运行的Photoshop风格的Web应用程序。该程序运行良好,速度非常快,直到我将混合模式添加到等式中。我通过将每个画布元素合并为一个并使用从底部画布开始的右混合模式组合每个画布中的每个像素来实现混合模式。
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);
用它调用不同混合模式的混合。我的“普通”混合模式如下:
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中的测试结果(在测试浏览器中产生了一些最佳效果)在画布620x385(238,700像素)上将三层混合在一起大约400毫秒。
这是一个非常小的实现,因为大多数项目的大小会更大,并且包含更多层,这将使执行时间在此方法下飙升。
我想知道是否有更快的方法将两个画布上下文与混合模式组合而不必遍历每个像素。
答案 0 :(得分:4)
不要创建这么多4值数组,使用现有内存时应该快得多。此外,您可能希望使用layer
数组上的reduce
function,这似乎正是您所需要的。但是,根本不使用任何功能可能会更快地触摸另一个 - 不需要创建执行上下文。以下代码将仅为每个图层调用混合函数,而不是每个像素*图层。
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
。这应该加快显示速度。只有我不确定这是否与其他效果一起使用,如果它们需要应用于多个图层。
答案 1 :(得分:2)
传统上,这些类型的大规模像素操作通过在GPU上而不是在CPU上运行来加速。 不幸的是,canvas不支持此功能,但您可以使用SVG Filters实现变通方法。这将允许您使用硬件加速混合模式(feBlend)将两个图像混合在一起。 如果将图层渲染为两个图像,然后在SVG中引用这些图像,则可以使其工作。
以下是一个很好的图解概述:它是如何工作的:
http://blogs.msdn.com/b/ie/archive/2011/10/14/svg-filter-effects-in-ie10.aspx(适用于IE10,但适用于任何支持SVG过滤器的浏览器)