2012-01-27 11 views
11

Aquí es lo que estoy tratando de hacer:Lienzo: ¿enmascarar una imagen y preservar su canal alfa?

  1. así tener una imagen, y la imagen B. La imagen B es una imagen de la máscara en blanco y negro.
  2. Reemplace el canal alfa de la imagen A con el canal rojo de la imagen B.
  3. Dibuje la imagen C en el lienzo.
  4. imagen Drenaje A en la parte superior de la imagen C.

Todo parece bien hasta el paso 4. La imagen C no es visible en absoluto, y donde la imagen A debe ser transparente hay de color blanco.

cx.putImageData(imageA, 0, 0); 
var resultData = cx.getImageData(0, 0, view.width, view.height); 

for (var h=0; h<resultData.data.length; h+=4) { 
    resultData.data[h+3] = imageB.data[h]; 
} 

cx.putImageData(imageC, 0, 0); 
cx.putImageData(resultData, 0, 0); 

Respuesta

16

Simon tiene razón: el método putImageData no presta atención a componer; simplemente copia los valores de píxel. Para obtener composición, necesitamos usar operaciones de dibujo.

Necesitamos meternos con los canales (convertir el rojo en alfa) con los datos de píxeles, poner esos datos de píxeles cambiados en una imagen, y luego usar una operación compuesta para obtener la máscara deseada.

Kinda lame

//copy from one channel to another 
var assignChannel = function(imageData, channelTo, channelFrom) { 
    if(channelTo < 0 || channelTo > 3 || channelFrom < 0 || channelFrom > 3) { 
    throw new Error("bad channel number"); 
    } 
    if(channelTo == channelFrom) 
    return; 
    var px = imageData.data; 
    for(var i = 0; i < px.length; i += 4) { 
    px[i + channelTo] = px[i + channelFrom]; 
    } 
}; 
/**============================================================================ 
    * this function uses 3 or 4 canvases for clarity/pedagogical reasons: 
    * redCanvas has our mask image; 
    * maskCanvas will be used to store the alpha channel conversion of redCanvas' image; 
    * imageCanvas contains the image to be masked; 
    * ctx is the context of the canvas to which the masked image will be drawn. 
============================================================================**/ 
var drawOnTopOfRed = function(redCanvas, maskCanvas, imageCanvas, ctx) { 
    var redImageData = redCanvas.getContext("2d").getImageData(0, 0, w, h); 

    //assign the alpha channel 
    assignChannel(redImageData, 3, 0); 

    //write the mask image 
    maskCanvas.getContext("2d").putImageData(redImageData, 0, 0); 

    ctx.save(); 

    //draw the mask 
    ctx.globalCompositeOperation = "copy"; 
    ctx.drawImage(maskCanvas, 0, 0); 

    //draw the image to be masked, but only where both it 
    //and the mask are opaque; see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing for details. 
    ctx.globalCompositeOperation = "source-in"; 
    ctx.drawImage(imageCanvas, 0, 0); 
    ctx.restore(); 
}; 

jsfiddle example

Un doodle con el ejemplo:

Kinda lame about the same

+0

Gracias, lea mi comentario sobre la respuesta de Simon :) Problema resuelto ... –

2

Debido a que en el paso 4 que está utilizando putImageData que sustituye perfectamente píxeles. Desea dibujar la imagen A encima de la imagen C, por lo que no puede hacer esto. En su lugar, tendrá que usar drawImage()

Así hace:

cx.putImageData(imageC, 0, 0); // step 3 
// create a new canvas and new context, 
// call that new context ctx2 and canvas can2: 
var can2 = document.createElement('canvas'); 
// set can2's width and height, get the context etc... 
ctx2.putImageData(resultData, 0, 0); 
cx.drawImage(can2, 0, 0); // step 4 using drawImage instead of putting image data 
+0

Vaya, no sabía que se puede pasar un objeto de la lona como un argumento para dibujarImage(). Esto funciona, pero por algún motivo drawImage() es mucho, mucho más lento que putImageData() y como necesito dibujar imágenes cada 20-40ms, no es una solución perfecta. Hice algunos experimentos y lo resolví usando dos lienzos. PutImageData() reemplaza completamente los valores de píxel, pero cuando pongo otro lienzo debajo del primero, puedo ver que es transparente. De todos modos, gracias por tu tiempo, ¡lo aprecio! –

+2

Eso es extraño. usar 'imageData' es casi siempre mucho más lento que usar' drawImage'. Puede haber algo más en juego aquí. ¿Puedo ver el código completo? –

+0

Te envié un correo. –

Cuestiones relacionadas