使用DataView和ArrayBuffer转换32BitPackedARGB数组以解决Endiness问题

时间:2015-01-10 11:55:40

标签: javascript arraybuffer argb

我这里有这个代码:

var can = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
can.width = '16';
can.height = '16';
var ctx = can.getContext('2d');
ctx.fillStyle = "rgb(255,0,0)";
ctx.fillRect(0, 0, 16, 16);

var ARGBpixmap = [16, 16];
var image_data = ctx.getImageData(0, 0, can.width, can.height);
for (var x = 0; x < image_data.width; x++) {
    for (var y = 0; y < image_data.height; y++) {
        var i = x * 4 + y * 4 * image_data.width;
        var r = image_data.data[i];
        var g = image_data.data[i + 1];
        var b = image_data.data[i + 2];
        ARGBpixmap.push(r + g + b);
    }
}

console.log(ARGBpixmap);

然而,它是耐心的。所以我听说我必须使用DataView和ArrayBuffer。这是我的尝试:

var can = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
can.width = '16';
can.height = '16';
var ctx = can.getContext('2d');
ctx.fillStyle = "rgb(255,0,0)";
ctx.fillRect(0, 0, 16, 16);

// Getting pixels as a byte (uint8) array
var imageData = ctx.getImageData(0, 0, can.width, can.height);
var pixels8BitsRGBA = imageData.data;
console.log(pixels8BitsRGBA.length)

var sizeOfEachStoredNumber = 4; //bytes
var numberOfStoresPerPixel = 4; //we have a r g and b
var numberOfPixels = can.width * can.height;
var buffer = new ArrayBuffer((numberOfPixels * (sizeOfEachStoredNumber *  numberOfStoresPerPixel))  + 8); // +8 bytes for the two leading 32 bytes integers
console.log((numberOfPixels * (sizeOfEachStoredNumber *  numberOfStoresPerPixel))  + 8);

//buffer size is: 256 repetitions of 4 numbers. each number is 4bytes. so 16 bytes per repetition. 256 reps. so 4096 total bytes is size
// Reverting bytes from RGBA to ARGB
//var pixels8BitsARGB = 
var view = new DataView(buffer, 0);
view.setUint8(0, can.width, true);
view.setUint8(4, can.height, true);
for(var i = 0 ; i < pixels8BitsRGBA.length ; i += 4) {
    var bytePos = i/4*16+8; //starting byte position of this group of 4 numbers // +8 bytes for the two leading 32 bytes integers
    //console.log(bytePos, h)
    var r = pixels8BitsRGBA[i  ];
    var g = pixels8BitsRGBA[i+1];
    var b = pixels8BitsRGBA[i+2];
    var a = pixels8BitsRGBA[i+3];

    view.setUint8(bytePos, a, true);
    view.setUint8(bytePos+4, r, true);
    view.setUint8(bytePos+8, g, true);
    view.setUint8(bytePos+12, b, true);
}

// Converting array buffer to a uint32 one, and adding leading width and height
var pixelsAs32Bits = new Uint32Array(buffer);

console.log(pixelsAs32Bits);

两者都是可复制的可粘贴代码,第一个代码的数字非常不同。我该如何修复我的代码?我对DataView和ArrayBuffer不太满意。

1 个答案:

答案 0 :(得分:1)

你真的不需要输入数组,但我想使用它们并没有什么坏处。

没有类型化数组,在第一个代码段中,

ARGBpixmap.push(r + g + b);

只需要

ARGBpixmap.push((r << 16) + (g << 8) + b);

这将3个字节转换为单个32位整数形式

00000000 RRRRRRRR GGGGGGGG BBBBBBBBB

您可能需要1或alpha值而不是0,具体取决于您要对数据执行的操作。要获取alpha值,请使用

var r = image_data.data[i];
var g = image_data.data[i + 1];
var b = image_data.data[i + 2];
var a = image_data.data[i + 3];
ARGBpixmap.push((a << 24) + (r << 16) + (g << 8) + b);

您可以使用此行获取无符号数,但它并不重要,因为低32位完全相同,请参阅http://www.codeonastick.com/2013/06/javascript-convert-signed-integer-to.html

ARGBpixmap.push(((a << 24) + (r << 16) + (g << 8) + b) >>> 0);

在第二个片段中

view.setUint8(bytePos, a, true);
view.setUint8(bytePos+4, r, true);
view.setUint8(bytePos+8, g, true);
view.setUint8(bytePos+12, b, true);

应该是

view.setUint8(i + 3, a, true);
view.setUint8(i + 2, r, true);
view.setUint8(i + 1, g, true);
view.setUint8(i + 0, b, true);

原因是现在您在源和目标上逐字节处理ARGB数据。我已经是一个字节位置,你不需要计算另一个。相关差异仅在每个4字节组内。

编辑:说明结束。