I want to convert an Uint8Array
of bytes that contains an ARGB image into its RGBA representation, however I would like to get that with something more opimized than what I propose here, using byte shifting for example.
What I do right now is to just swap the bytes order in the sense that (for example, but length can be any multiple of 4):
let input = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 ];
let expected = [0x22, 0x33, 0x44, 0x11, 0x88, 0x55, 0x66, 0x77 ];
So the simple case would be:
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;
}
However, ideally I'd like to build the RGBA value using byte shifting (and that's the trivial part), but my problem is then how to put such uint32 number into the dest
(which must be an Uint8Array
and ideally even the same of src so to do inplace-changes).
I mean, in C once I've my uint32_t rgbaColor
defined, I can just memwrite
or assign the int to the array index and the overflow will do the rest.
But how do I do this in JS?
Edit: After some tests, it looks that the initial proposal is still the fastest, in fact
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>