Is it possible to read pixels of an image in canvas A and create pixels on canvas B? And I want to create the new pixels on Canvas B only where the image's pixels are green.
eg. If images' pixel (120,45) is green I need to create a green colored pixel in Canvas B at (120,45)
You can use canvas ImageData to get color values for each pixels. The function:
context.getImageData(left, top, width, height);
returns an ImageData object, which consists of the following properties:
width
height
data (CanvasPixelArray)
The CanvasPixelArray contains the RGBA values for all the pixels, starting from top-left working its way to bottom-right. So in other words, it is an array containing 4*width*height number of entries.
Using that, you can start looping through each pixel (+=4 entries per pixel), and check the RGBA values. If they match a specific value you want, i.e. green, then you would copy that value to a canvas B, which you would create by modifying a newly created ImageData object.
You can create a new empty ImageData object with:
context.createImageData(cssWidth, cssHeight)
Note that you will need to know specific RGBA values that identify "green" or define specific conditions, i.e. if the G value of RGBA is >= 150 then it is "green".
Also note that you cannot get the ImageData that has been tainted resources outside of your origin. i.e., if you draw an image onto the canvas that isn't CORS safe, you won't be able to retrieve the ImageData from that canvas anymore, much like how you can't use toDataURL either.
Related
I am retrieving pixels in a canvas imagedata and I'm doing that a lot.
I think the inserting and retrieving from and to the canvas imagedata is expensive in cpu time, so I want to make as few of those as possible.
One way of cutting that would be to make a single insert that would insert multiple pixels in a single sequence, but so far I have not been able to see how that would be done. All the examples I have seen so far retrieve and insert only a single pixel.
So the question is,
in order to speed up canvas imagedata pixel manipulation, how do I insert/retrieve multiple pixels simultaneously?
Just select a larger region when retrieving a pixel buffer:
var imageData = ctx.getImageData(x, y, width, height);
^^^^^^^^^^^^ not limited to one
Now your data buffer will contain all pixels for the given region. To get the whole canvas:
var imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
Adjust them and put back into the same position:
ctx.putImageData(imageData, x, y);
and you're done.
Remember that each pixel consists of four bytes (RGBA). To address a larger buffer you can do:
function getPixelIndex(x, y) {
return (y * width + x) * 4; // width used when getting buffer
}
Tips:
if you plan to update the same buffer often simply retrieve the buffer once and store a pointer to it, update it when you need and put back, then reuse the same buffer. This way you save the time getting the buffer. This won´t work if you in the mean time apply graphics to the canvas with standard methods.
You can also start with an empty buffer using createImageData() instead of getImageData().
If your pixel color data is more or less static you can update the buffer using a Uint32Array instead of the Uint8ClampedArray. You get a 32-bit version like this after getting the imageData:
var buffer32 = new Uint32Array(imageData.data.buffer);
Your new buffer32 will point to the same underlying byte buffer so no significant memory overhead, but it allows you to read and write 32-bit values instead of just 8-bit. Just be aware of that the byte order is (typically) little-endian so order the bytes as ABGR. Then do as before, call ctx.putImageData(imageData, x, y); when you need to update.
Can a JavaScript canvas be manipuled like a standard bitmap (accessing/modifying a pixel and getting it's size)? Is this use optimized, or would it be faster to manipulate normal 2d arrays of pixels and draw over canvas when you need it?
Absolutely yes! Please have a look here:
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Pixel_manipulation_with_canvas
As for your second question, as per the pixels documentation, pixels is a one dimensional array. You'll need to do your own 2 x 2 hoopla for a 2d way.
Taken from a previous SO answer by I82Much (works dandy for me):
int row = i;
int col = j;
int offset = row * width + col;
color p = pixels[offset];
More here: how to loop over the pixels using 2D array?
and here: http://www.processing.org/reference/pixels.html
You can get an array of pixel data from the canvas context using getImageData(). Bear in mind that each pixel takes-up 4 spaces in the array (for red, green, blue and alpha). Then, once you've altered the array to your liking, you can use putImageData() to put the data back.
Is it possible to get an array of pixels from a PNG or BMP image in Javascript? I'd like to obtain an RGB array from an image so that I can manipulate the array of pixels, and then draw the modified image on a canvas.
UPDATE: I found an exact duplicate here: how to get the RGB value of an image in a page using javascript?
However, the answers don't go into much detail about how to actually solve this problem.
There are a hundred tutorials on the net about using HTML Canvas imageData, which gets the RGBA values of an canvas. Do a search for canvas imageData and you'll find plenty of info.
All you have to do is:
ctx.drawImage(img, 0, 0);
var imgData = ctx.getImageData(x, y, width, height).data;
imgData is now an array where every 4 places are each pixel. So [0][1][2][3] are the [r][g][b][a] of the first pixel.
I have a shape (a quarter circle) that I've created using the html canvas function:
moveTo
LineTo
QuadraticCurveTo
How do I go about exploding the shape into particles and then return them to form a circle?
I'm not going to write the code for you because it will take some time, and I'm sure you can find examples on the web, but I'll tell you the theory you need to know in order to make such a thing.
Create an in-memory canvas (using document.createElement('canvas')) that will never be seen on the page. This canvas must be at least as large as your object. I'm going to assume it is exactly as large as your object. We'll call this tempCanvas and it has tempCtx
Draw your object to tempCtx.
There will be some event that you didn't mention exactly but I'm sure you have in mind. Either you press a button or click on the object and it "explodes". For the sake of picking something I'll assume you want it to explode on click.
So to do the explosion:
Draw the object onto your normal context: ctx.drawImage(tempCanvas, x, y) so the user sees something
You're going to want to have an array of pixels for the location of each pixel in tempCanvas. So if tempCanvas is 20x30 you'll want an array of [20][30] to correspond.
You have to keep data for each of those pixels. Specifically, their starting point, which is easy, because pixel [2][4]'s starting point is (2,4)! And also their current location, which is identical to starting point at first but will change on each frame.
When the explosion event occurs keep track of the original mouse x and y position.
At this point for every single pixel you have a vector which means you have a direction. If you clicked in the middle of the object you'll want to save the mouse coordinates of (10,15) (see note 1). So now all of the pixels of the to-be-exploded image have their trajectory. There's a bit of math here that I'm taking for granted, but if you search separate topics either on SO or on the internet you'll find out how to find the slope/etc of these lines and continue them.
For every frame hereafter you must take each pixel [x][y] and use ctx.drawImage(tempCanvas, x, y, 1, 1, newX, newY, 1, 1) where x and y are the same as the pixel's [x][y] and the newX and newY are calculated using the vector and finding what the next point would be along its line.
The result will be each pixel of the image being drawn in a location that is slightly more away from the original click point. If you continue to do this frame after frame it will look as if the object has exploded.
That's the general idea, anyway. Let me know if any of it is unclear.
note 1: Most likely your normal canvas won't be the same size as the to-explode object. Maybe the object is placed at 100,100 so you really clicked on 110, 115 instead of 10,15. I'm omitting that offset just for the sake of simplicity.
I am using the easelJS library with multiple bitmapSequence objects on my canvas. I may be asking the wrong question, because I don't understand how this works. I will try to explain my situation as clearly as I can.
I am placing multiple bitmapSequence objects (sprite animation sequences) on the canvas and moving them within the global tick() function, by setting the x and y properties. Once I set their x and y properties, I call stage.update(), which re-renders the canvas and all of the bitmapSequence objects with their new locations.
After the stage.update() call, but still within the tick() function, I assign the variable ctx to canvas.getContext('2d'). Then I call ctx.fillRect(0, 0, 8, 8). In this case the 0,0 (x,y) arguments for fillRect ALWAYS represents the origin point for the very last bitmapSequence object of which I modified the x and y attributes of prior to the stage.update() call.
This means if I draw a rectangle at 0,0 it will be show at the origin of the very last bitmapSequence object I used, and follow the bitmapSequence when I move it).
If I try to get a 2d context, and draw a rectangle prior to the stage.update() it does not show up on the canvas.
Ideally I would like to be able to draw rectangles relative to the origin of any bitmapSequence object I wish. Please help me understand what I am misunderstanding.
Maybe you are looking for translate() function? The behaviour of your program corresponds to behaviour of that function. So, if you want to reset the relative drawing, use ctx.translate(-x_of_last_bitmapSequence, -y_of_last_bitmapSequence).
Alternatively you can change the "starting point" of relative drawing:
ctx.save();
ctx.translate(x, y);
ctx.strokeRect(0, 0, 30, 30) // strokes a square at coords [x, y]
ctx.restore(); // restores the original state (relative coords are at [0, 0])
I was able to use the easelJS's Container object to achieve this instead. It allowed me to add objects to the container, and objects within the container moved with the container. Objects within the container had their x/y coordinates relative to the container, and the container had x/y coordinates relative to the canvas. It worked just as I expected it to.
I still don't know what is up with the 2D context in conjunction with easelJS.