2012-08-03 3 views
5

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

다른 블렌드 모드를 위해 블렌드를 호출합니다. 크롬에서

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 (238,700 픽셀)에 함께 세 개의 레이어를 혼합 다음과 같이 내 '정상'혼합 모드입니다.

대부분의 프로젝트는 크기가 더 크고이 방법으로 실행 시간이 급격하게 늘어나는 레이어가 더 많으므로 아주 작은 구현입니다.

모든 픽셀을 거치지 않고 두 캔버스 컨텍스트를 혼합 모드로 결합하는 더 빠른 방법이 있는지 궁금합니다.

+0

'nextLayerPixel'이란 무엇입니까? 당신은 그것을 어떻게 만들고'blend' 함수 (두번째 매개 변수)에서 그것을 변경합니까? – Bergi

+0

나는 처음에는 그 부분을 배제하여 여분의 코드가 없으면 기능을 보여주지 못했지만 이제는 추가했다. 'nextLayerPixel'은 단순히 각 레이어의 동일한 픽셀을 참조하는 변수이다. 따라서 3 개의 레이어와 픽셀 x : 30, y : 20의 프로젝트에서는 하단 레이어 픽셀을 30,20, 중간 30,20, 상단 30,20에서 가져옵니다. –

답변

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

대체 솔루션 : 모든 자바 스크립트에서 함께 레이어를 혼합하지 마십시오. 그냥 절대적으로 서로 캔버스를 배치하고 그들에게 CSS를 부여 opacity. 이렇게하면 표시 속도가 빨라집니다. 단지 당신이 다른 효과와 함께 작동할지, 여러개의 레이어에 적용해야하는지 잘 모르겠습니다.

+0

아주 좋은 답변입니다. 내 코드를 실제로 사용하기 위해 reduce 함수를 얻으려면 문서를 읽는 동안 잠시만 요. 나는 속도가 가장 빠른 브라우저에서 180ms로, 느린 브라우저에서 1000ms로 내려 가기 때문에 무언가를 알아 내야 할 것입니다. 모바일 브라우저는 1 초 이상입니다.이것은 내 응용 프로그램에서 작동하지 않으며 한 번에 더 큰 영역을 편집 할 수있는 다른 방법이 없으면 블렌드 모드를 종료해야 할 수도 있습니다. –

+0

얼마나 자주이 블렌드 함수를 호출해야합니까? – Bergi

+0

혼합 함수는 프로젝트의 레이어 중 하나가 일반 모드가 아닌 한 레이어 중 하나가 수정 될 때마다 호출됩니다. 수정 된 영역 만 호출하지만 사용자가 대형 브러시를 사용하여 대형 객체 나 페인팅을 이동하면 매우 빠르게 넓은 영역을 수행해야하며 그래픽을 이동할 때마다 호출됩니다. 이것은 단지 mousedown/mouseup이 아닙니다. 매초마다 여러 번 호출되는 mousemove도 포함됩니다. 적어도 5fps (200ms)로 설정할 수 있으면 작동하지만 느린 브라우저에서는 이러한 현상이 나타나지 않습니다. –

2

전통적으로 이러한 유형의 방대한 픽셀 조작은 CPU 대신 GPU에서 실행함으로써 가속화됩니다. 캔버스는이 기능을 지원하지 않지만 SVG Filters을 사용하여 해결 방법을 구현할 수 있습니다. 이렇게하면 하드웨어 가속 혼합 모드 (feBlend)를 사용하여 두 이미지를 혼합 할 수 있습니다. 레이어를 두 개의 이미지로 렌더링 한 다음 SVG에서 이러한 이미지를 참조하면이 작업을 수행 할 수 있습니다. 여기

이가 일할 수있는 방법 좋은 예시 개요입니다

http://blogs.msdn.com/b/ie/archive/2011/10/14/svg-filter-effects-in-ie10.aspx (IE10에 대한하지만 SVG 필터를 지원하는 모든 브라우저에 적용)