【问题标题】:JavaScript optimized version of ARGB to RGBA array conversionJavaScript 优化版 ARGB 到 RGBA 数组转换
【发布时间】:2020-03-11 14:38:03
【问题描述】:

我想将包含 ARGB 图像的 Uint8Array 字节转换为其 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('------------------------------------------------------');
}
&lt;script src="https://rawgit.com/eu81273/jsfiddle-console/master/console.js"&gt;&lt;/script&gt;

【问题讨论】:

  • 对于那些不太熟悉 Uint8Array 的人,您能否提供一个传入和传出值的示例?我可能有一个答案的想法,但如果没有正确回答,至少你是有教育意义的。
  • @MisterJojo 好的,我刚刚添加了。
  • @MisterJojo 是的,只是一个错字(已修复),但是...当然可以,但我想要一个使用位移位的优化版本
  • @MisterJojo 请检查更新后的帖子,看起来最初提议的版本仍然是迄今为止最快的......
  • 你没有测试我的“第一个”提案(函数名为 c_ARGB_2_RGBA_8 -> const c_ARGB_2_RGBA_8=([A1,R1,G1,B1,A2,R2,G2,B2])=&gt;[R1,G1,B1,A1,R2,G2,B2,A2]...

标签: javascript colors bit-shift dataview uint8array


【解决方案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; 也可以内联进行更改。

【讨论】:

  • 不幸的是,这比建议的要慢,看起来数据访问没有按预期优化
【解决方案2】:

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

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

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(', ')  } ]` 
&lt;pre id="AnswerProof"&gt;&lt;/pre&gt;

【讨论】:

    【解决方案3】:

    我对此进行了自己的速度测试。 在我的 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-08-22
      • 1970-01-01
      • 2012-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多