A 5 by 5 pixel image data is something like this in linearized imagedata array-
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 255, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
So, the 3x3 pixel data is- 0 0 0 255. How can I get the adjacent pixel positions? Left and right adjacent ones are easy, just minus 4 and plus 4 respectively.
The pixel data from .getImageData().data
is a TypedArray
of type Uint8ClampedArray
. When reading the values they will be in the range 0-255 and in the order Red, Green, Blue, Alpha. If the value of alpha is zero then red, green, and blue will also be zero.
To get the index of a pixel
const imageData = ctx.getImageData(0,0,ctx.canvas.width, ctx.canvas.height);
var index = (x + y * imageData.width) * 4;
const red = imageData.data[index];
const green = imageData.data[index + 1];
const blue = imageData.data[index + 2];
const alpha = imageData.data[index + 3];
To move down one pixel
index += imageData.width * 4;
To move up one
index -= imageData.width * 4;
To move left.
index -= 4;
To move right
index += 4;
If you are on the left or right edge and you move in the direction of the edge you will wrap around, on the line above and to the right if moving left and the line below and on the left if moving down.
When setting the image data the values will be floored and clamped to 0-255
imageData.data[index] = 29.5
console.log(imageData.data[index]); // 29
imageData.data[index] = -283
console.log(imageData.data[index]); // 0
imageData.data[index] = 283
console.log(imageData.data[index]); // 255
If you set an index that is outside the array size it will be ignored
imageData.data[-100] = 255;
console.log(imageData.data[-100]); // Undefined
imageData.data[imageData.data.length + 4] = 255;
console.log(imageData.data[imageData.data.length + 4]); // Undefined
You can speed up access and processing by using different array types. For example all of a pixel's channels as one value using Uint32Array
const imageData = ctx.getImageData(0,0,ctx.canvas.width, ctx.canvas.height);
const pixels = new Uint32Array(imageData.data.buffer);
var index32 = x + y * imageData.width; // note there is no 4*;
const pixel = pixels[index32];
The channels are stored in bits 31-24 Alpha, 23-16 Blue, 15-8 Green, 7-0 Red.
You can set a pixel using a hex value
pixels[x + y * imageData.width] = 0xFF0000FF; // red
pixels[x + y * imageData.width] = 0xFF00FF00; // Green
pixels[x + y * imageData.width] = 0xFFFF0000; // Blue
pixels[x + y * imageData.width] = 0xFF000000; // Black
pixels[x + y * imageData.width] = 0xFFFFFFFF; // White
pixels[x + y * imageData.width] = 0; // Transparent
You can set all the pixels in a single call
pixels.fill(0xFF000000); // all pixels black
You can copy array data onto the array with
// set 3 pixels in a row at x,y Red, Yellow, White
pixels.set([0xFF0000FF,0xFF00FFFF,0xFFFFFFFF], x+y * imageData.width);
If the canvas has any pixel/s that are from an untrusted source it will be tainted and you will not be able to read the pixel data. Trusted sources are same domain or images served with the appropriate CORS header information. Images that are on the file system can not have their pixels accessed. Once a canvas is tainted it can not be cleaned.
A tainted canvas will throw an error when you call ctx.getImageData(0,0,1,1,)
MDN does not list this exception for some reason. You will see "SecurityError" DOMException; in the DevTools console and there are plenty of answered question here in StackOverflow on the subject.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With