6

我正在设计一个在 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 中的测试结果(在测试的浏览器中产生了一些最好的)大约 400 毫秒,在 620x385(238,700 像素)的画布上将三层混合在一起。

这是一个非常小的实现,因为大多数项目的规模会更大,并且包含更多的层,这将使执行时间在这种方法下飙升。

我想知道是否有更快的方法将两个画布上下文与混合模式结合起来,而不必遍历每个像素。

4

2 回答 2

3

不要创建这么多的 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。这应该会大大加快显示速度。只有我不确定这是否会与您的其他效果一起使用,如果它们需要应用于多个图层。

于 2012-08-03T04:12:27.943 回答
2

传统上,这些类型的大规模像素操作是通过在 GPU 上而不是在 CPU 上运行来加速的。不幸的是,画布不支持此功能,但您可以使用SVG Filters实现一种解决方法。这将允许您使用硬件加速混合模式 (feBlend) 将两个图像混合在一起。如果您将图层渲染为两个图像,然后在 SVG 中引用这些图像,您就可以完成这项工作。

这是一个很好的图解概述,它是如何工作的:

http://blogs.msdn.com/b/ie/archive/2011/10/14/svg-filter-effects-in-ie10.aspx(适用于 IE10,但适用于任何支持 SVG 过滤器的浏览器)

于 2012-08-03T02:42:33.227 回答