2012-08-03 5 views
5

HTML5 Canvas要素で動作するPhotoshopスタイルのWebアプリケーションを設計しています。プログラムはうまく動作し、混合モードを方程式に追加するまでは非常に高速です。各キャンバス要素を1つにマージし、各キャンバスの各ピクセルを下のキャンバスから右のブレンドモードを使用して組み合わせることで、ブレンドモードを実現します。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); 

異なるブレンドモードのブレンドを呼び出します。 Chromeで

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; 
} 

私のテスト結果は約400msでました(テストブラウザのうち、最高のいくつかをもたらす)キャンバス620x385(238700ピクセル)に合わせて三つの層をブレンド:次のように私の「通常」ブレンドモードです。

これは非常に小さな実装です。ほとんどのプロジェクトのサイズが大きくなり、この方法では実行時間が急増するレイヤーが増えるためです。

すべてのピクセルを通過することなく、2つのキャンバスコンテキストをブレンドモードで結合する方法があれば疑問に思っています。

+0

「nextLayerPixel」とは何ですか?どのようにしてそれを作成しますか?なぜあなたは 'blend'関数(2番目のパラメータ)でそれを変更しますか? – Bergi

+0

私は最初にその部分を除外して、余分なコードを付け加えなくても機能を表示しましたが、これを追加しました。 'nextLayerPixel'は単に各層の同じピクセルを参照する変数です。したがって、3つのレイヤーとピクセルx:30、y:20のプロジェクトでは、30,20,20,30,20の順に下位レイヤピクセルを取得します。 –

答えて

4

ように多くの4価値の配列を作成しないでください、それ既存のメモリを使用するとはるかに高速になるはずです。またreduce functionlayer配列に使用することもできます。これはまさに必要なものです。ただし、関数を全く使用しないと、別のタッチが高速になる可能性があります。実行コンテキストを作成する必要はありません。次のコードは、各ピクセル*レイヤではなく、各レイヤに対してのみブレンド関数を呼び出します。

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。これは表示を高速化するはずです。複数のレイヤーに適用する必要がある場合、これが他のエフェクトと一緒に機能するかどうかはわかりません。

+0

非常に良い答えです。私のコードで実際に動作するようにreduce関数を得るためにドキュメントを読んでいるうちに私はしばらく時間をとった。私はまだ何かを理解しなければならないでしょう。スピードが最速のブラウザでは180msに、低速のブラウザでは1000msになるからです。モバイルブラウザは1秒以上です。これは私のアプリケーションでは機能しません。一度に大きな領域を編集する別の方法がない限り、ブレンドモードを解除する必要があります。 –

+0

このブレンド関数をどのくらいの頻度で呼び出す必要がありますか? – Bergi

+0

ブレンド機能は、プロジェクトのいずれかのレイヤーが通常モード以外のレイヤーを持つ限り、レイヤーの1つが変更されるたびに呼び出されます。それは変更された領域だけを呼び出しますが、大きなブラシを使用して大きなオブジェクトやペイントを移動している場合は、非常に迅速に大きな領域を実行する必要があり、グラフィックを移動するたびに呼び出されます。これは単にマウス/マウスアップではありません。また、毎秒何回も呼び出されるmousemoveも含まれています。これは、少なくとも5fps(200ms)にすることができたらうまくいくかもしれませんが、遅いブラウザーでは起こっていません。 –

2

伝統的に、これらの種類の大量のピクセル操作は、CPUではなくGPUで実行することで高速化されています。 残念ながらキャンバスにはこれがサポートされていませんが、SVG Filtersを使用して回避策を実装する可能性があります。これにより、ハードウェアアクセラレーションされたブレンドモード(feBlend)を使用して2つのイメージをブレンドすることができます。 レイヤーを2つのイメージにレンダリングし、SVGでこれらのイメージを参照すると、この作業を行うことができます。ここで

が、これは仕事ができるか素敵図示概要です:(IE10のためではなく、SVGフィルタをサポートする任意のブラウザに適用されます)

http://blogs.msdn.com/b/ie/archive/2011/10/14/svg-filter-effects-in-ie10.aspx