-1

I have this code here:

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);

However it is endiness. So I heard I have to use DataView and ArrayBuffer. This was my attempt:

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);

Both are copy pasteable code, the first one gets very different numbers. How can I fix my code? I'm not that great with DataView and ArrayBuffer.

halfer
  • 19,824
  • 17
  • 99
  • 186
Noitidart
  • 35,443
  • 37
  • 154
  • 323

1 Answers1

1

You don't really need typed arrays, but I guess it doesn't hurt to use them.

Without typed arrays, in the first snippet,

ARGBpixmap.push(r + g + b);

just needs to be

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

This converts 3 bytes into a single 32 bit integer of the form

00000000 RRRRRRRR GGGGGGGG BBBBBBBBB

You may want 1s or the alpha value instead of the 0s, depending on what you are going to do with the data. To get the alpha value, use

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);

You may be able to get an unsigned number using this line, but it should not really matter, as the low 32 bits are exactly the same, see http://www.codeonastick.com/2013/06/javascript-convert-signed-integer-to.html

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

In the second snippet

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

should probably be

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

The reason is that now you are dealing with the ARGB data byte by byte at the source and target. i is a byte position already, you don't need to calculate another. The relevant differences are only within each 4-byte group.

EDIT: Account for endianess.

Stefan Haustein
  • 18,427
  • 3
  • 36
  • 51
  • But `bytePos` holds the starting byte number of the group. So it starts at 8 and ends at 4100. – Noitidart Jan 10 '15 at 12:11
  • You don't really need bytePos in that case (edited accordinly) – Stefan Haustein Jan 10 '15 at 12:17
  • Thanks very much for your help. If I try the `<<` stuff i get an array of `Array [ 16, 16, -65536, -65536, -65536, -65536, -65536, -65536, ...` The `-65536` is definitely wrong, it should be unsigned right? Thanks man so much! – Noitidart Jan 10 '15 at 12:24
  • I'm real sure I have to put it at byte positions thats how I managed it here: https://gist.github.com/Noitidart/e3d9f61bd268368df37a and also i think thats what the docs say on DataView: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView – Noitidart Jan 10 '15 at 12:28
  • 1
    -65536 >>> 0 = 4294901760 -- is that the number you are looking for? It doesn't really matter for ARGB if it is signed or unsigned, as long as the bits are the same... – Stefan Haustein Jan 10 '15 at 12:30
  • 1
    Yes, you have to put the bytes at byte positions, but i is a byte position already. – Stefan Haustein Jan 10 '15 at 12:31
  • Thanks very much I think that number is right! I tried your way of putting +1 2 3, and i got an array of `[16,16,65535,0,0,0,....]` for a total length of 1026 items. Shouldn't doing `new Uint32Array(buffer)` return a 257 length array? – Noitidart Jan 10 '15 at 12:40
  • I just tested it by feeding the array to this code here: https://gist.github.com/Noitidart/a1541a6968d25ecaaefc it came out all unrecognizable :( We didnt do it right :( – Noitidart Jan 10 '15 at 13:26
  • 65535 is just cyan with alpha 0. Maybe you need to invert the order in the second example to account for big/little endian? Also, you get 1026 items because you multiply with 4 two times. You only have one byte per r, g, b, a, not 4 each. So you need to multiply by 4 only once, not by 16. – Stefan Haustein Jan 10 '15 at 13:40
  • Hm wow a very interesting thing, i was messing with it, and instead of encoding on one computer and testing on another, i did the encoding on same computer and the colors look like they come out fine. But the picture is rotated. Here is image of ![](http://i.imgur.com/2RFfMR4.png) the top one is correct, the bottom one is wrong. The top one is the actual icon, the bottom one is me making data. – Noitidart Jan 10 '15 at 13:50
  • Well then just do two nested loops over x and y (like in the first snippet) and adjust the source or target index accordingly... – Stefan Haustein Jan 10 '15 at 14:02
  • Thanks Stefan! It works! I switched the y loop on top this is my working code: https://gist.github.com/Noitidart/74b29c37444b7ae4b861 Can you please help me to get the same result with DataView i really want to learn that by applying it. I think im so close. – Noitidart Jan 10 '15 at 14:17
  • 1
    Just replace `ARGBpixmap.push()` with `view.setUint8(j++, b, true); view.setUint8(j++, g, true); ...`. Initialized j with 8 before the loop. – Stefan Haustein Jan 10 '15 at 14:25
  • Ok ill try that out and let you know how it works :) – Noitidart Jan 10 '15 at 21:52