Hello I've implemented a function in which I pass an array of imageData
of png images (rgba) with transparent parts. I flatten the images with the code bellow but I have an issue regarding the alpha and I could use some help. As you may see in the screenshot bellow the red text has a semi transparent black shadow but instead it is rendered white and not semi transparent.
var mergeImageData = function ( imageDataArray ) {
var canvas = document.getElementById( 'canvas' );
canvas.width = 512;
canvas.height = 512;
var ctx = canvas.getContext( '2d' );
var newImageData = imageDataArray[ 0 ];
for ( var j = 1, len = imageDataArray.length; j < len; j++ ) { // iterate through the imageDataArray
console.log( imageDataArray[ j ].data.length );
for ( var i = 0, bytes = imageDataArray[ j ].data.length; i < bytes; i += 4 ) { // iterate through image bytes
var index = ( imageDataArray[ j ].data[ i + 3 ] === 0 ? 0 : j );
newImageData.data[ i ] = imageDataArray[ index ].data[ i ];
newImageData.data[ i + 1 ] = imageDataArray[ index ].data[ i + 1 ];
newImageData.data[ i + 2 ] = imageDataArray[ index ].data[ i + 2 ];
newImageData.data[ i + 3 ] = imageDataArray[ index ].data[ i + 3 ];
}
}
ctx.putImageData( newImageData, 0, 0 );
console.log( "all done" );
};
IMPORTANT
This will be done in a webworker
in the future, that's why I'm interested in this method since web workers have no canvas access.
Your first issue is with putImageData()
function : According to this answer by @ellisbben,
the putImageData method does not pay any attention to compositing; it merely copies pixel values. In order to get compositing, we need to use drawing operations.
So you have to make use of a second canvas, in memory (as well described in this answer by @markE).
Then, your second issue is that you want to do it in a webworker.
You're right, webworkers don't seem able to process such drawing operations : according to this answer by @AshleysBrain
Web workers can only calculate, not modify the DOM or make any calls to draw to a canvas.
So your only way is to split your operations by processing datas by the worker, then draw it into your main thread.
Here is an example, assuming that you've got 2 png files called respectively test0.png & test1.png in your root folder.
index.html
<html>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="main.js"></script>
</html>
main.js
if (!!window.Worker) {
var myWorker = new Worker("worker.js");
myWorker.onmessage = function(e) {
var tmp=document.createElement("canvas");
tmp.width=512;
tmp.height=512;
var ctx2=tmp.getContext("2d");
ctx2.putImageData(e.data[0],0,0);
var canvas = document.getElementById( 'canvas' );
canvas.width = 512;
canvas.height = 512;
var ctx = canvas.getContext( '2d' );
ctx.fillStyle = "rgb(0,255, 0)";
ctx.fillRect(0,0,250,50);
ctx.drawImage(tmp,0,0);
}
}
var images = [];
function getImagesData(){
for(i=0; i<2; i++){
var img = new Image();
img.src = "test"+i+".png";
//Should add an onload method
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
images[i] = ctx.getImageData(0,0,img.width,img.height);
}
myWorker.postMessage(images);
}
getImagesData();
Worker.js
var mergeImageData = function( imageDataArray ) {
var newImageData = imageDataArray[ 0 ];
for ( var j = 0; j < imageDataArray.length; j++ ) { // iterate through the imageDataArray
for ( var i = 0, bytes = imageDataArray[ j ].data.length; i < bytes; i += 4 ) { // iterate through image bytes
var index = ( imageDataArray[ j ].data[ i + 3 ] === 0 ? 0 : j );
newImageData.data[ i ] = imageDataArray[ index ].data[ i ];
newImageData.data[ i + 1 ] = imageDataArray[ index ].data[ i + 1 ];
newImageData.data[ i + 2 ] = imageDataArray[ index ].data[ i + 2 ];
newImageData.data[ i + 3 ] = imageDataArray[ index ].data[ i + 3 ];
}
}
return newImageData;
};
onmessage = function(e) {
var res = mergeImageData(e.data);
postMessage([res]);
}
Original png files
Result