3

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.

enter image description here

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.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
0x_Anakin
  • 3,229
  • 5
  • 47
  • 86
  • Just a guess - this line: newImageData.data[ i + 3 ] = imageDataArray[ index ].data[ i + 3 ]; just sets the tranparancy to the current PNG, should it not just set it to 1? – JMP Jan 23 '15 at 11:39
  • the values are between 0 and 255, if I set it to 1 the flatten image is invisible – 0x_Anakin Jan 23 '15 at 12:00
  • at least you don't get a white outline :-) – JMP Jan 23 '15 at 13:40
  • It is because `putImageData()` doesn't pay attention to compositing, it only reads pixels. You will need to `putImageData()` in an in-memory canvas, then `drawImage()` this canvas to your actual rendered one. – Kaiido Jan 23 '15 at 14:01
  • possible duplicate of [putImageData(), how to keep old pixels if new pixels are transparent?](http://stackoverflow.com/questions/22194267/putimagedata-how-to-keep-old-pixels-if-new-pixels-are-transparent) – Kaiido Jan 23 '15 at 14:01
  • @Kaiido in a web worker putImageData and drawImage are not available if I'm not mistaken. – 0x_Anakin Jan 23 '15 at 14:38
  • I think you're right but you only pass the datas to your worker, make the `putImageData()` / `drawImage()` in the main scope (e.g in `onmessage`) – Kaiido Jan 23 '15 at 15:32
  • but this is what I want to do on the workers – 0x_Anakin Jan 23 '15 at 15:40
  • 1
    you want to blend textures together. And you prefer multi threading over hardware acceleration. Interesting. – Winchestro Jan 23 '15 at 16:02
  • @Winchestro I wasn't aware of that... is it faster if i do this for 400 images x 3 images on a single thread instead of using workers ? – 0x_Anakin Jan 23 '15 at 20:59
  • @Syd I doubt you can load 400 x 3 images into memory at the same time unless those are very tiny images. That aside, assuming you process and then store them in a database or something, loading them should take way longer than processing to the point where there shouldn't even be a noticeable lag. – Winchestro Jan 24 '15 at 23:10

2 Answers2

1

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
test0.png enter image description here

Result
Result

Community
  • 1
  • 1
Kaiido
  • 123,334
  • 13
  • 219
  • 285
-2

http://robertleeplummerjr.github.io/CanvasWorker or http://github.com/robertleeplummerjr/CanvasWorker

15000 pngs load in under a second ;)

Robert Plummer
  • 634
  • 1
  • 5
  • 13