使用网络工作者和画布合并 png 图像

Merge png images using webworkers and canvas

本文关键字:合并 png 图像 布合并 网络 工作者      更新时间:2023-09-26

我试图利用网络工作者将一个png数组合并到一个具有透明度的图像中,就像Photoshop合并多个图层时所做的那样。

这样做的原因是因为我将组合的结果保存到 indexeddb 中以供离线使用,然后我在我的 three.js 应用程序中使用该图像创建纹理。

场景是大约有 400 种产品,纹理 - 3d 平面(产品)上的图像是 3 张图像(产品标题、价格和产品图像)的混合。

到目前为止,我已经实现了这个,但是图像处理不是那么好,我想知道如果我通过网络工作者imageData.data

这是我到目前为止所拥有的,但我无法让它工作。(这是在没有网络工作者的情况下完成的,现在只是试图使合并工作)

var images = [];
var imageData = [];
images.push( function ( callback ) {
    var mobile = new Image();
    mobile.onload = function () {
        console.log( "mobile loaded" );
        callback( null, mobile );
    };
    mobile.onerror = function ( e ) {
        console.log( "mobile error" );
        callback( e, null );
    };
    mobile.src = "mobile.png";
} );
images.push( function ( callback ) {
    var text = new Image();
    text.onload = function () {
        console.log( "text loaded" );
        callback( null, text );
    };
    text.onerror = function ( e ) {
        console.log( "text error" );
        callback( e, null );
    };
    text.src = "text.png";
} );
async.series( images, function ( err, results ) {
    if ( err ) {
        console.error( err );
    } else {
        console.log( results );
        getImageData( results );
    }
} );

var getImageData = function ( images ) {

    for ( var i = 0; i < images.length; i++ ) {
        var canvas = document.createElement( 'canvas' );
        canvas.width = 512;
        canvas.height = 512;
        var ctx = canvas.getContext( '2d' );
        ctx.drawImage( images[ i ], 0, 0 );
        imageData.push( ctx.getImageData( 0, 0, canvas.width, canvas.height ).data );
    }
    merge( imageData );
};
var merge = function ( data ) {
    var merged = [];
    var mixFactor = 0.5;
    //var newPixel = imageMainPixel * mixFactor + imageSecPixel * ( 1 - mixFactor )
    //for ( var i = 0, len = data.length; i < len; i++ ) {
    for ( var j = 0, byteLen = data[ 0 ].length; j < byteLen; j += 4 ) {
        var r = data[ 0 ][ j ] * mixFactor + data[ 1 ][ j ] * ( 1 - mixFactor );
        var g = data[ 0 ][ j + 1 ] * mixFactor + data[ 1 ][ j + 1 ] * ( 1 - mixFactor );
        var b = data[ 0 ][ j + 2 ] * mixFactor + data[ 1 ][ j + 2 ] * ( 1 - mixFactor );
        var a = data[ 0 ][ j + 3 ] * mixFactor + data[ 1 ][ j + 3 ] * ( 1 - mixFactor );
        merged[ j ] = r;
        merged[ j + 1 ] = g;
        merged[ j + 2 ] = b;
        merged[ j + 4 ] = a;
    }
    //}
    var canvas = document.getElementById( 'canvas' );
    canvas.width = 512;
    canvas.height = 512;
    var ctx = canvas.getContext( '2d' );
    var imageData = ctx.createImageData( 512, 512 );
    imageData.data.set( merged );
    ctx.putImageData( imageData, 0, 0 );
};

我目前得到的错误是这样的:

Uncaught RangeError: Source is too large

更新

我设法通过将我的合并功能更改为以下内容来合并图像:

var merge = function ( data ) {
    var canvas = document.getElementById( 'canvas' );
    canvas.width = 512;
    canvas.height = 512;
    var ctx = canvas.getContext( '2d' );
    var imageData = ctx.createImageData( 512, 512 );
    var mixFactor = 0.5;

    for ( var j = 0, byteLen = data[ 0 ].length; j < byteLen; j += 4 ) {
        imageData.data[ j ] = data[ 0 ][ j ] * mixFactor + data[ 1 ][ j ] * ( 1 - mixFactor );
        imageData.data[ j + 1 ] = data[ 0 ][ j + 1 ] * mixFactor + data[ 1 ][ j + 1 ] * ( 1 - mixFactor );
        imageData.data[ j + 2 ] = data[ 0 ][ j + 2 ] * mixFactor + data[ 1 ][ j + 2 ] * ( 1 - mixFactor );
        imageData.data[ j + 3 ] = data[ 0 ][ j + 3 ] * mixFactor + data[ 1 ][ j + 3 ] * ( 1 - mixFactor );
    }

    console.log( imageData.data.length );
    ctx.putImageData( imageData, 0, 0 );
};

结果就像 2 张图像,两张图像都具有 50% 的不透明度。不过,这不是预期的结果。我只想让图像充当图层,例如将多个 png 一个放在另一个之上......如果上面的那个有透明的地方,你应该能够看到它下面的图像。

也许我应该根据 alpha 值绘制?

经过一番思考,我最终检查了每个像素的alpha值,并设法完成了我想要的。我像这样重写了合并函数:

var merge = function ( data ) {
    var canvas = document.getElementById( 'canvas' );
    canvas.width = 512;
    canvas.height = 512;
    var ctx = canvas.getContext( '2d' );
    var imageData = ctx.createImageData( 512, 512 );
    var mixFactor = 0.5;

    for ( var j = 0, byteLen = data[ 0 ].length; j < byteLen; j += 4 ) {
        // imageData.data[ j ] = data[ 0 ][ j ] * mixFactor + data[ 1 ][ j ] * ( 1 - mixFactor );
        // imageData.data[ j + 1 ] = data[ 0 ][ j + 1 ] * mixFactor + data[ 1 ][ j + 1 ] * ( 1 - mixFactor );
        // imageData.data[ j + 2 ] = data[ 0 ][ j + 2 ] * mixFactor + data[ 1 ][ j + 2 ] * ( 1 - mixFactor );
        // imageData.data[ j + 3 ] = data[ 0 ][ j + 3 ] * mixFactor + data[ 1 ][ j + 3 ] * ( 1 - mixFactor );
        imageData.data[ j ] = ( data[ 1 ][ j + 3 ] === 0 ? data[ 0 ][ j ] : data[ 1 ][ j ] );
        imageData.data[ j + 1 ] = ( data[ 1 ][ j + 3 ] === 0 ? data[ 0 ][ j + 1 ] : data[ 1 ][ j + 1 ] );
        imageData.data[ j + 2 ] = ( data[ 1 ][ j + 3 ] === 0 ? data[ 0 ][ j + 2 ] : data[ 1 ][ j + 2 ] );
        imageData.data[ j + 3 ] = ( data[ 1 ][ j + 3 ] === 0 ? data[ 0 ][ j + 3 ] : data[ 1 ][ j + 3 ] );
    }

    console.log( imageData.data.length );
    ctx.putImageData( imageData, 0, 0 );
};

仍然需要一些改进,但我想现在我将能够在 Web worker 中合并图像:)

修改后的代码为:

for ( var j = 0, byteLen = data[0].length; j < byteLen; j += 4 )
{
  var rA = data[0][j],   rB = data[1][j];
  var gA = data[0][j+1], gB = data[1][j+1];
  var bA = data[0][j+2], bB = data[1][j+2];
  var aA = data[0][j+3], aB = data[1][j+3];
  var rOut = (rA * aA / 255) + (rB * aB * (255 - aA) / (255*255));
  var gOut = (gA * aA / 255) + (gB * aB * (255 - aA) / (255*255));
  var bOut = (bA * aA / 255) + (bB * aB * (255 - aA) / (255*255));
  var aOut = aA + (aB * (255 - aA) / 255);
  imageData.data[j] = rOut;
  imageData.data[j+1] = gOut;
  imageData.data[j+2] = bOut;
  imageData.data[j+3] = aOut;
} 

这是解决方案(阿尔法混合)如何混合两个 ARGB 像素?

我写了一个处理这个问题的库。不仅如此,它还处理阴影和瓷砖。