Basically, I am trying to achieve a high-pass filter for my canvas application. The concept is simple: take the regular image pixels minus the pixels of the same image blurred with a radius of x.
Seems simple, right?
var d = pixel.data;
var blurdata = blur(amnt, pixel);
var bd = blurdata.data;
for (var i=0; i<d.length; i+=4) {
d[i] = 128+(d[i]-bd[i]);
d[i+1] = 128+(d[i+1]-bd[i+1]);
d[i+2] = 128+(d[i+2]-bd[i+2]);
}
return pixel;
The code is run with 2 parameters, amnt and pixel. Pixel is the ctx.getImageData object, and amnt is the blur radius. The blur function returns an imagedata object as well. The blur code is not the issue though. That works 100% as expected. What the problem is, is that it seems that the d and bd variables are the same. I'm not sure why. When bd[i] is subtracted from d[i] the result is 0, which when added to 128 makes for a completely gray image when the data is put back into the canvas. Oh, and the blurdata and pixel data have the same dimensions.
Any help really appreciated.
Blur function: http://www.files.croar.net/f/9/
Your data is the same because your data is the same. When you blur pixel.data in the blur function it is also changing the values in the array d. What you'll probably want to do is make two canvases with the same image. Have d be the pixel data for the unblurred image and then blur the data for the other image:
var composite = canvas1.getContext("2d").getImageData();
var d = composite.data;
var blurdata = blur(amnt, pixel);
var bd = blurdata.data;
for (var i=0; i<d.length; i+=4) {
d[i] = 128+(d[i]-bd[i]);
d[i+1] = 128+(d[i+1]-bd[i+1]);
d[i+2] = 128+(d[i+2]-bd[i+2]);
}
return composite;
I think that should get the job done.
Related
I work on a personal project to try out equations to try to simulate the behavior of a galaxy. I have so far managed to place the Points as I wanted, but now I want to take each point individually to change its position.
The goal for now is just to successfully try to apply a Random Vector to each of the points.
I tried:
var direction = new THREE.Vector3(0.00003, 0.000005, 0);
points.position.add(direction);
but this applies to all Points.
Then I tried something like that:
for (let i = 0; i < points.geometry.attributes.position.count; i++) {
points.geometry.attributes.position[i] = Math.random() * 500
}
points.geometry.attributes.position.needsUpdate = true;
But nothing append :( I thing I missed something but I dind't know what
Here the full code on codepen:
Codepen
When you access:
points.geometry.attributes.position[i]
you're not getting the array of the vertex positions. You're getting the BufferAttribute. What you probably want is the array inside the BufferAttribute:
points.geometry.attributes.position.array[i]
However, this is still not the recommended approach. Three.js recommends you use the .getAttribute() method:
// Get the attribute
const posAttribute = points.geometry.getAttribute("position");
// Get the array inside the attribute
const posArray = posAttribute.array;
// Increment by 3 at a time to access XYZ separately
for(let i3 = 0; i3 < posArray.length; i3 += 3) {
posArray[i3 + 0] = xPosition;
posArray[i3 + 1] = yPosition;
posArray[i3 + 2] = zPosition;
}
// Tell the attribute it needs updatin
posAttribute.needsUpdate = true;
<img src="circle.png" onclick="alert('clicked')"/>
Let's imagine that circle.png is a 400x400 px transparent background image with a circle in the middle.
What I've got now is that the entire image area (400x400px) is clickable. What I would like the have is that only the circle (non transparent pixels) are clickable.
Of course I know that in this example I could use the <map> tag and a circular area, but I'm looking for a general solution which will take into consideration actual image transparency and work for any kind of images (i.e. non regular shapes).
The most complex way I could see is to trace the contour of the image basing on each pixel alpha, convert to a path (maybe simplify) and apply as a map.
Is there a more efficient / straightforward way to do so?
Using the canvas tag, you can determine the color value of a pixel under a given spot. You can use the event data to determine the coordinates, then check for transparency. All that remains is loading the image up into a canvas.
First, we'll take care of that:
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.onload = function(){
ctx.drawImage(img,0,0);
};
img.src = [YOUR_URL_HERE];
This first bit grabs the canvas element, then creates an Image object. When the image loads, it is drawn on the canvas. Pretty straightforward! Except... if the image is not on the same domain as the code, you're up against the same-domain policy security. In order to get the data of our image, we'll need the image to be locally hosted. You can also base64 encode your image, which is beyond the scope of this answer. (see this url for a tool to do so).
Next, attach your click event to the canvas. When that click comes in, we'll check for transparency and act only for non-transparent click regions:
if (isTransparentUnderMouse(this, e))
return;
// do whatever you need to do
alert('will do something!');
The magic happens in the function isTransparentUnderMouse, which needs two arguments: the target canvas element (this in the click handler's scope) and the event data (e, in this example). Now we come to the meat:
var isTransparentUnderMouse = function (target, evnt) {
var l = 0, t = 0;
if (target.offsetParent) {
var ele = target;
do {
l += ele.offsetLeft;
t += ele.offsetTop;
} while (ele = ele.offsetParent);
}
var x = evnt.page.x - l;
var y = evnt.page.y - t;
var imgdata = target.getContext('2d').getImageData(x, y, 1, 1).data;
if (
imgdata[0] == 0 &&
imgdata[1] == 0 &&
imgdata[2] == 0 &&
imgdata[3] == 0
){
return true;
}
return false;
};
First, we do some dancing around to get the precise position of the element in question. We're going to use that information to pass to the canvas element. The getImageData will give us, among other things, a data object that contains the RGBA of the location we specified.
If all those values are 0, then we're looking at transparency. If not, there's some color present. -edit- as noted in the comments, the only value we really need to look at is the last, imgdata[3] in the above example. The values are r(0)g(1)b(2)a(3), and transparency is determined by the a, alpha. You could use this same approach to find any color at any opacity that you know the rgba data for.
Try it out here: http://jsfiddle.net/pJ3MD/1/
(note: in my example, I used a base64 encoded image because of the domain security I mentioned. You can ignore that portion of the code, unless you also intend on using base64 encoding)
Same example, with changes to the mouse cursor thrown in for fun: http://jsfiddle.net/pJ3MD/2/
Documentation
Image object on MDN - https://developer.mozilla.org/en/DOM/Image
Tutorial for using images with canvas on MDN - https://developer.mozilla.org/en/Canvas_tutorial/Using_images
Canvas portal on MDN - https://developer.mozilla.org/en/HTML/Canvas
HTML canvas element on MDN (getContext) - https://developer.mozilla.org/en/DOM/HTMLCanvasElement/
CanvasRenderingContext2D on MDN (getImageData) - https://developer.mozilla.org/en/DOM/CanvasRenderingContext2D
Pixel manipulation on MDN - https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas/
You can do this using HTML5 canvas. Draw the image in to the canvas, attach a click handler to the canvas, and in the handler, check if the pixel that was clicked on is transparent.
thank you chris for this great answer
I added a few lines to the code to handle with scaling the canvas
so what I do now is creating a canvas of the exact pixel size the image has that is drawn on it. like (for an Image 220px*120px):
<canvas width="220" height="120" id="mainMenu_item"></canvas>
and scale the canvas using css:
#mainMenu_item{
width:110px;
}
and the adjusted isTransparentUnderMouse function looks like:
var isTransparentUnderMouse = function (target, evnt) {
var l = 0, t = 0;
if (target.offsetParent) {
var ele = target;
do {
l += ele.offsetLeft;
t += ele.offsetTop;
} while (ele = ele.offsetParent);
}
var x = evnt.pageX - l;
var y = evnt.pageY - t;
var initialWidth = $(evnt.target).attr('width');
var clientWidth = evnt.target.clientWidth;
x = x * (initialWidth/clientWidth);
var initialHeight = $(evnt.target).attr('height');;
var clientHeight = evnt.target.clientHeight;
y = y * (initialHeight/clientHeight);
var imgdata = target.getContext('2d').getImageData(x, y, 1, 1).data;
if (
imgdata[0] == 0 &&
imgdata[1] == 0 &&
imgdata[2] == 0 &&
imgdata[3] == 0
){
return true;
}
return false;
};
I have a (largish) HTML5 canvas. Its rendering a pictures from a file, using context.drawImage() and this is quite fast. (Note that there are more than one picture on the same canvas).
Now I need to perform some manipulations to the pixels on the canvas, basically I need to perform Alpha Blending which darkens certain areas of the picture. So instead I used this approach.
//create an invisible canvas so that we don't do the actual rendering of the image
var invisibleCanvas = document.createElement('canvas');
invisibleCanvas.width = myWidth;
invisibleCanvas.height = myHeight;
var invContext = invisibleCanvas.getContext('2d');
invContext.drawImage(imageObj, 0, 0, invisibleCanvas.width, invisibleCanvas.height);
var imageData = invContext.getImageData(0, 0, invisibleCanvas.width, invisibleCanvas.height)
var pixelComponents = imageData.data;
var dkBlendingAmount = 0.5;
for (var i = 0; i < pixelComponents.length; i += 4)
{
//there are a few extra checks here to see if we should do the blending or not
pixelComponents[i] = pixelComponents[i] * dkBlendingAmount;
pixelComponents[i+1] = pixelComponents[i+1] * dkBlendingAmount;
pixelComponents[i+2] = pixelComponents[i+2] * dkBlendingAmount;
}
//this is the real place where I want it
context.putImageData(imageData, xOffset, yOffset);
Is there a way to make this faster? Is there a way to get the image data directly from my imageObj rather than having to put it on a canvas, get the data, convert it and put it on another canvas?
As a very inexperienced programmer, I'm trying to code a game that detects when the player collides with certain colors on the canvas. I have a black square with coordinates "player.x" and "player.y" and dimensions 50x50 that moves around when you press the arrow keys. I also have a stationary red (255,0,0) square elsewhere on the canvas.
The function below is supposed to grab a slightly larger square around the "player" square and find out if there's any red in it. If there is, it will send up an alert. The problem is, this doesn't seem to be working.
function collideTest(){
var canvas = document.getElementById("canvas");
var c = canvas.getContext("2d");
var whatColor = c.getImageData(player.x - 5, player.y - 5,60,60);
for (var i = 0; i < 3600; i++) {
if (whatColor.data[i] == 255) {
alert("red");
}
}
}
I'm semi-aware that this is not the most efficient way to detect red pixels, but I wanted to simplify the code before posting it here. Is there something obviously wrong with the function?
The problem could lie in the way the function is called. It gets called at the end of another function that detects user-input and changes the coordinates of the "player" square. THAT function gets called right before everything is drawn on the canvas.
Thanks in advance for any help!
var whatColor = c.getImageData(player.x - 5, player.y - 5,60,60);
player.x and player.y must not be decimal, make sure they are rounded or getImageData will be angry and not play nice.
For each single pixel on the canvas, the whatColor.data array holds 4 sequential pieces of color information: red,green,blue,alpha(opacity). So the whatColor.data looks like this for each pixel:
whatColor.data[i] is the red component of the color.
whatColor.data[i+1] is the green component of the color.
whatColor.data[i+2] is the blue component of the color.
whatColor.data[i+3] is the alpha(opacity) component of the color.
So your iteration would look like this (4 indexes per pixel):
for(var i = 0, n = whatColor.data.length; i < n; i += 4) {
var red = whatColor.data[i];
var green = whatColor.data[i + 1];
var blue = whatColor.data[i + 2];
var alpha = whatColor.data[i + 3];
if(red==255){ ... it's a hit, do your thing! ... }
}
See here for a mini-tutorial on the imageData.data array: http://www.html5canvastutorials.com/advanced/html5-canvas-get-image-data-tutorial/
By the way, you might look at one of the canvas libraries that simplify game making with canvas. Here are just a few: easelJs, KineticJs, FabricJs, and more!
I'm currently working on a basic javascript game that has two sprites that are not to be collided together. However, basic bounding box collision won't suffice as there are portions of the sprites that are transparent and wouldn't 'count' as colliding. I found a solution to the problem that I am having, but I can't get it to work. What I would like to do is calculate the transparent portions of the sprites and make sure that if the transparent portions overlap, that there is no collision detected. Here is what I found that solves the problem.
http://blog.weeblog.net/?p=40#comments
/**
* Requires the size of the collision rectangle [width, height]
* and the position within the respective source images [srcx, srcy]
*
* Returns true if two overlapping pixels have non-zero alpha channel
* values (i.e. there are two vissible overlapping pixels)
*/
function pixelCheck(spriteA, spriteB, srcxA, srcyA, srcxB, srcyB, width, height){
var dataA = spriteA.getImageData();
var dataB = spriteB.getImageData();
for(var x=0; x<width; x++){
for(var y=0; y<height; y++){
if( (dataA[srcxA+x][srcyA+y] > 0) && (dataB[srcxB+x][srcyB+y] > 0) ){
return true;
}
}
}
return false;
}
And for calculating the image data:
/**
* creating a temporary canvas to retrieve the alpha channel pixel
* information of the provided image
*/
function createImageData(image){
$('binaryCanvas').appendTo('body');
var canvas = document.getElementById('binaryCanvas');
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var imageData = [image.width];
for(var x=0; x<image.width; x++){
imageData[x] = [image.height];
for(var y=0; y<image.height; y++){
var idx = (x + y * image.width) * 4;
imageData[x][y] = canvasData.data[idx+3];
}
}
$("#binaryCanvas").remove();
return imageData;
}
The problem is that I don't know how to implement this solution, or if this is the best solution to my problem. Is this what I'm looking for? And if so, where do I put these methods? The thing that I'm most confused about is what I should be passing to spriteA and spriteB. I've tried passing Images and I've tried passing the imageData returned from the pixelCheck method, but receiving the same error: that the object or image has no method 'getImageData'. What am I doing wrong?
Two things are wrong with this.
You made a function:
function createImageData(image){...}
But what you are calling is:
spriteA.getImageData();
spriteB.getImageData();
The dot notates a property of an object. You were trying to call a function that was never part to the objects. There are some simple fixes.
add the createImageData() function to your constructor :
function Sprite(){
...
this.createImageData = function(image){...};
...
}
or :
Sprite.createImageData = function(image{...};
or just call it correctly:
createImageData(spriteA.image); // or whatever the image is
Second, your function calls for an image parameter, but you aren't supplying one. Simply remember to supply the image when you call it. You could also remove the parameter and get the image from within the function.
Sort of like this:
function Sprite(){
...
this.createImageData = function(){
var image = this.image;
// or just use this.image, or whatever it is
...
}
...
}
Hope this helped.