Is it possible to query a HTML Canvas object to get the color at a specific location?
There's a section about pixel manipulation in the W3C documentation.
Here's an example on how to invert an image:
var context = document.getElementById('myCanvas').getContext('2d');
// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;
// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = 255 - pix[i ]; // red
pix[i+1] = 255 - pix[i+1]; // green
pix[i+2] = 255 - pix[i+2]; // blue
// i+3 is alpha (the fourth element)
}
// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);
Try the getImageData
method:
var data = context.getImageData(x, y, 1, 1).data;
var rgb = [ data[0], data[1], data[2] ];
idx = (y * width + x) * 4
like Georg's answer. However, don't forget to refresh that cached object every time the image changes.
Color()
constructor? That doesn't seem to exist anywhere
Yes sure, provided you have its context. (See how to get canvas context here.)
var imgData = context.getImageData(0,0,canvas.width,canvas.height)
// { data: [r,g,b,a,r,g,b,a,r,g,..], ... }
function getPixel(imgData, index) {
var i = index*4, d = imgData.data
return [d[i],d[i+1],d[i+2],d[i+3]] // Returns array [R,G,B,A]
}
// AND/OR
function getPixelXY(imgData, x, y) {
return getPixel(imgData, y*imgData.width+x)
}
PS: If you plan to mutate the data and draw them back on the canvas, you can use subarray
var
idt = imgData, // See previous code snippet
a = getPixel(idt, 188411), // Array(4) [0, 251, 0, 255]
b = idt.data.subarray(188411*4, 188411*4 + 4) // Uint8ClampedArray(4) [0, 251, 0, 255]
a[0] = 255 // Does nothing
getPixel(idt, 188411) // Array(4) [0, 251, 0, 255]
b[0] = 255 // Mutates the original imgData.data
getPixel(idt, 188411) // Array(4) [255, 251, 0, 255]
// Or use it in the function
function getPixel(imgData, index) {
var i = index*4, d = imgData.data
return imgData.data.subarray(i, i+4) // Returns subarray [R,G,B,A]
}
You can experiment with this on http://qry.me/xyscope/, the code for this is in the source, just copy/paste it in the console.
context.getImageData(x, y, 1, 1);
while
with a storage to avoid stack overflow with too much recursion.
function GetPixel(context, x, y)
{
var p = context.getImageData(x, y, 1, 1).data;
var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
return hex;
}
function rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
Yup, check out getImageData()
. Here's an example of breaking CAPTCHA with JavaScript using canvas:
OCR and Neural Nets in JavaScript
Note that getImageData
returns a snapshot. Implications are:
Changes will not take effect until subsequent putImageData
getImageData and putImageData calls are relatively slow
//Get pixel data
var imageData = context.getImageData(x, y, width, height);
//Color at (x,y) position
var color = [];
color['red'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 0];
color['green'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 1];
color['blue'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 2];
color['alpha'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 3];
You can use i << 2
.
const data = context.getImageData(x, y, width, height).data;
const pixels = [];
for (let i = 0, dx = 0; dx < data.length; i++, dx = i << 2) {
pixels.push({
r: data[dx ],
g: data[dx+1],
b: data[dx+2],
a: data[dx+3]
});
}
Fast and handy
Use following class which implement fast method described in this article and contains all you need: readPixel
, putPixel
, get width/height
. Class update canvas after calling refresh()
method. Example solve simple case of 2d wave equation
class Screen{ constructor(canvasSelector) { this.canvas = document.querySelector(canvasSelector); this.width = this.canvas.width; this.height = this.canvas.height; this.ctx = this.canvas.getContext('2d'); this.imageData = this.ctx.getImageData(0, 0, this.width, this.height); this.buf = new ArrayBuffer(this.imageData.data.length); this.buf8 = new Uint8ClampedArray(this.buf); this.data = new Uint32Array(this.buf); } // r,g,b,a - red, gren, blue, alpha components in range 0-255 putPixel(x,y,r,g,b,a=255) { this.data[y * this.width + x] = (a << 24) | (b << 16) | (g << 8) | r; } readPixel(x,y) { let p= this.data[y * this.width + x] return [p&0xff, p>>8&0xff, p>>16&0xff, p>>>24]; } refresh() { this.imageData.data.set(this.buf8); this.ctx.putImageData(this.imageData, 0, 0); } } // -------- // TEST // -------- let s=new Screen('#canvas'); function draw() { for (var y = 1; y < s.height-1; ++y) { for (var x = 1; x < s.width-1; ++x) { let a = [[1,0],[-1,0],[0,1],[0,-1]].reduce((a,[xp,yp])=> a+= s.readPixel(x+xp,y+yp)[0] ,0); let v=a/2-tmp[x][y]; tmp[x][y]=v<0 ? 0:v; } } for (var y = 1; y < s.height-1; ++y) { for (var x = 1; x < s.width-1; ++x) { let v=tmp[x][y]; tmp[x][y]= s.readPixel(x,y)[0]; s.putPixel(x,y, v,v,v); } } s.refresh(); window.requestAnimationFrame(draw) } // temporary 2d buffer ()for solving wave equation) let tmp = [...Array(s.width)].map(x => Array(s.height).fill(0)); function move(e) { s.putPixel(e.x-10, e.y-10, 255,255,255);} draw();
If you want to extract a particular color of pixel by passing the coordinates of pixel into the function, this will come in handy:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function detectColor(x, y){
data=ctx.getImageData(x, y, 1, 1).data;
col={
r:data[0],
g:data[1],
b:data[2]
};
return col;
}
x
, y
is the coordinate you want to filter out color.
var color = detectColor(x, y)
The color is the object, you will get the RGB value by color.r
, color.g
, color.b
.
Success story sharing