0

我想将Uint8Array包含 ARGB 图像的字节转换为它的 RGBA 表示,但是我想用比我在这里建议的更优化的东西来获得它,例如使用字节移位。

我现在所做的只是在某种意义上交换字节顺序(例如,但长度可以是 4 的任意倍数):

let input = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 ];
let expected = [0x22, 0x33, 0x44, 0x11, 0x88, 0x55, 0x66, 0x77 ];

所以简单的情况是:

function argbToRgba(src) {
    let dest = new Uint8Array(src.length)

    for (let i = 0; i < src.length; i += 4) {
        let a = src[i]
        let r = src[i + 1]
        let g = src[i + 2]
        let b = src[i + 3]

        dest[i] = r;
        dest[i + 1] = g;
        dest[i + 2] = b;
        dest[i + 3] = a;
    }

    return dest;
}

但是,理想情况下,我想使用字节移位来构建 RGBA 值(这是微不足道的部分),但我的问题是如何将这样的 uint32 数字放入dest(必须是一个Uint8Array,理想情况下甚至与 src 相同)进行就地更改)。

我的意思是,在 C 中,一旦我uint32_t rgbaColor定义好了,我就可以memwrite将 int 分配给数组索引,然后溢出来完成剩下的工作。但是我如何在 JS 中做到这一点?


编辑:经过一些测试,看起来最初的提案仍然是最快的,实际上

function argbToRgbaDataView(src, inline=false) {
    let dest = inline ? src : new Uint8Array(src.length);
    let srcView = new DataView(src.buffer);
    let destView = new DataView(dest.buffer);

    for (let i = 0; i < src.length; i += 4) {
        let argb = srcView.getUint32(i);
        let rgba = (argb & 0x00FFFFFF) << 8 |
                   (argb & 0xFF000000) >>> 24;
        destView.setUint32(i, rgba);
    }

    return dest;
}

function argbToRgbaDataViewInline(src) {
    return argbToRgbaDataView(src, true);
}

function argbToRgbaSwap(src, inline=false) {
    let dest = inline ? src : new Uint8Array(src.length);

    for (let i = 0; i < src.length; i += 4) {
        let a = src[i]
        let r = src[i + 1]
        let g = src[i + 2]
        let b = src[i + 3]

        dest[i] = r;
        dest[i + 1] = g;
        dest[i + 2] = b;
        dest[i + 3] = a;
    }

    return dest;
}

function argbToRgbaSwapInline(src) {
    return argbToRgbaSwap(src, true);
}

function argbToRgbaSwapNoVars(src, inline = false) {
    let dest = inline ? src : new Uint8Array(src);

    for (let i = 0; i < src.length; i += 4) {
        let a = src[i]

        dest[i] = src[i + 1];
        dest[i + 1] = src[i + 2];
        dest[i + 2] = src[i + 3];
        dest[i + 3] = a;
    }

    return dest;
}

function argbToRgbaSwapNoVarsInline(src) {
    return argbToRgbaSwapNoVars(src, true);
}

// From https://stackoverflow.com/a/60639510/210151
function argb2rgbaStackOverflow(inArr) {
    return inArr.reduce((a, c, i, t) => {
        if (i % 4 === 0) {
            let [A, R, G, B] = t.slice(i, i + 4)
            a.push(R, G, B, A)
        }
        return a
    }, [])
}

function measureFunction(func) {
    let preTime = new Date().getTime();
    let ret = func.call(...arguments);
    console.log(`Calling ${func.name} took ${new Date().getTime() - preTime}ms`);
    return ret;
}

function createRandomArray(size) {
    return new Uint8Array(size).fill().map((a, i) =>
        a = i).sort(() => Math.random() - 0.5);
}

function iconSizeToBytes(iconSize) {
    const bytesPerPixel = 4;
    return iconSize * iconSize * bytesPerPixel;
}

// This is to add support to console.log to gjs
try {
    console;
} catch(e) {
    window.console = {
        log: function() { print(...arguments) },
    };
}

let allSizes = [
    iconSizeToBytes(32),
    iconSizeToBytes(64),
    iconSizeToBytes(512),
    iconSizeToBytes(1024),
    iconSizeToBytes(2048),
];

for (let size of allSizes) {
    console.log(`Creating random array of ${size/(1024 * 1024)}Mbyte...`);
    let randomArray = measureFunction(createRandomArray, size);

    measureFunction(argbToRgbaDataView, randomArray);
    measureFunction(argbToRgbaDataViewInline, randomArray);
    measureFunction(argbToRgbaSwap, randomArray);
    measureFunction(argbToRgbaSwapInline, randomArray);
    measureFunction(argbToRgbaSwapNoVars, randomArray);
    measureFunction(argbToRgbaSwapNoVarsInline, randomArray);
    measureFunction(argb2rgbaStackOverflow, randomArray);

    console.log('------------------------------------------------------');
}
<script src="https://rawgit.com/eu81273/jsfiddle-console/master/console.js"></script>

4

3 回答 3

1

看起来,在写这篇文章时,我发现了这个DataView,这确实简化了我的生活!

所以问题可以通过以下方式解决:

function argbToRgba(src) {
    let dest = new Uint8Array(src.length);
    let srcView = new DataView(src.buffer);
    let destView = new DataView(dest.buffer);

    for (let i = 0; i < src.length; i += 4) {
        let argb = srcView.getUint32(i);
        let rgba = (argb & 0x00FFFFFF) << 8 |
                   (argb & 0xFF000000) >>> 24;
        destView.setUint32(i, rgba);
    }

    return dest;
}

使用let dest = src;更改也可以内联完成。

于 2020-03-11T14:38:03.787 回答
0

我想我找到了最快的解决方案。

它在很大程度上受到了您的启发,但不是每次调用都重新创建一个新的 Uint8Array,而是直接在对象本身上完成A ( A rgb -> rgb A
) 的位移。

function fRGB_x(xInOut)
  {
  for (let i = 0; i < xInOut.length; i += 4)
    {
    let x0 = xInOut[i]
    xInOut[i]    = xInOut[i +1];  // R
    xInOut[i +1] = xInOut[i +2];  // G
    xInOut[i +2] = xInOut[i +3];  // B
    xInOut[i +3] = x0;            // A
    }
  }

let bob = new Uint8Array(8).map((_,i)=>i)

AnswerProof.textContent = `before -> [ ${bob.join(', ')  } ]`  

fRGB_x(bob)

AnswerProof.textContent += `\n\nafter --> [ ${bob.join(', ')  } ]` 
<pre id="AnswerProof"></pre>

于 2020-03-12T18:26:45.180 回答
0

我对此进行了自己的速度测试。在我的 Linux 计算机上,Firefox 和 Chromium 的速度奖励是不同的!

你可以自己测试:

function fRGB_0(src)  // optimized
  {
  let dest = new Uint8Array(src.length)
  for (let i = 0; i < src.length; i += 4)
    {
    dest[i]    = src[i +1];  // R
    dest[i +1] = src[i +2];  // G
    dest[i +2] = src[i +3];  // B
    dest[i +3] = src[i];     // A
    }
  return dest;
  }
function fRGB_x(xInOut)
  {
  for (let i = 0; i < xInOut.length; i += 4)
    {
    let x0 = xInOut[i]
    xInOut[i]    = xInOut[i +1];  // R
    xInOut[i +1] = xInOut[i +2];  // G
    xInOut[i +2] = xInOut[i +3];  // B
    xInOut[i +3] = x0;            // A
    }
  }
const twoMb = 2 * 1024 * 1024
  ,   Uint8  =_=>new Uint8Array(8).map(e=>Math.floor(Math.random()*256))
  ,   fRGB_1 =([A1,R1,G1,B1,A2,R2,G2,B2])=>[R1,G1,B1,A1,R2,G2,B2,A2]
  ,   fRGB_2 =inArr=>inArr.reduce((a,c,i,t)=>{if(i%4===0){let [A,R,G,B]=t.slice(i,i+4);a.push(R,G,B,A)}return a},[])
  ;

console.log('generate 2Mb Array for testing...')
let ArrayTest = []
for(let i=twoMb;i--;)  ArrayTest.push( Uint8() );

console.log('start test RGB_0')
console.time('Test RGB_0')
for(let i=twoMb;i--;)  { let bob = fRGB_0(ArrayTest[i]) }
console.timeEnd('Test RGB_0')

console.log('start test RGB_x')
console.time('Test RGB_x')
for(let i=twoMb;i--;)  { fRGB_x(ArrayTest[i]) }
console.timeEnd('Test RGB_x')

console.log('start test RGB_1')
console.time('Test RGB_1')
for(let i=twoMb;i--;)  { let bob = fRGB_1(ArrayTest[i]) }
console.timeEnd('Test RGB_1')

console.log('start test RGB_2')
console.time('Test RGB_2')
for(let i=twoMb;i--;)  { let bob = fRGB_2(ArrayTest[i]) }
console.timeEnd('Test RGB_2')
.as-console-wrapper { max-height: 100% !important; top: 0; }

于 2020-03-12T17:16:49.903 回答