fill with an image in processing.js - javascript

I'm working with some canvas and processing.js but i cant figure out how to fill an arc/ellipse etc with an image.
Using JavaScript usually i do something like this:
ctx.save();
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
ctx.drawImage(thumbImg, 0, 0, 400, 400);
ctx.beginPath();
ctx.arc(x, y, size, Math.PI * 2, true);
ctx.clip();
ctx.closePath();
ctx.restore();
and the game is done, but how can i do it with processing.js?
I've tried those options but seems that I'm doing something wrong:
b = loadImage("nicola.png");
fill(b)
background(b);
ellipse(x, y, size, size);
any idea?

I believe that what you are trying to get at is called image masking
an example of masking
Description:
Masks part of an image from displaying by loading another image and using it as an alpha channel. This mask image should only contain grayscale data, but only the blue color channel is used. The mask image needs to be the same size as the image to which it is applied.
In addition to using a mask image, an integer array containing the alpha channel data can be specified directly. This method is useful for creating dynamically generated alpha masks. This array must be of the same length as the target image's pixels array and should contain only grayscale data of values between 0-255.
Example:
var g2;
var setup = function(){
createCanvas(200,200);
background(0, 0, 0, 0);
smooth();
fill(255, 255, 255);
ellipse(100, 100, 200, 200);
var g1 = get(0, 0, 200, 200);
background(0, 0, 0, 0);
noStroke();
for(let i = 0; i < 360; i++){
fill(sin(radians(i))*255, i, 200);
rect(0, i, 200, 1);
}
g2 = get(0, 0, 200, 200);
g2.mask(g1);
}
var draw = function(){
background(255, 255, 255);
image(g2, 0, 0);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
an image of what the above code returns:

You can either use img.mask(maskImg) to apply an (pixel based) alpha mask or use img.blend(…) as described here for example.

A semicolon ';' is missing after fill(b)
So it should be fill(b);
I hope this solves your problem.

Related

How to change opacity for circles on canvas

So what I'm trying to do here is make multiple circles on my canvas and individually change the opacity of each of them. Here's what I tried so far, I know that I'm definitely putting the interval in the wrong place but I'm very confused as to how these canvases work exactly:
function makeCircle(x,y)
{
canvas.beginPath();
canvas.arc(x, y, 20, 0, 2 * Math.PI);
color = generateRandomColor();
canvas.fillStyle = color;
canvas.fill();
canvas.lineWidth = 0;
canvas.strokeStyle = color;
setInterval(function()
{
if(canvas.globalAlpha>=.05)
{
canvas.globalAlpha-=.05;
console.log("here");
}
else
{
canvas.globalAlpha = 0;
console.log(canvas.globalAlpha);
clearInterval();
}
}, 2000)
canvas.stroke();
}
Doing this does not change the opacity of the circles at all as far as I can tell.
Not sure what you are trying to do with that nested setInterval, or why use it at all...
Here is what I would do:
function makeCircle(x, y, color, alpha) {
canvas.beginPath();
canvas.globalAlpha = alpha
canvas.arc(x, y, 20, 0, 2 * Math.PI);
canvas.fillStyle = color;
canvas.fill();
}
var canvas_doc = document.getElementById("canvas");
var canvas = canvas_doc.getContext("2d");
makeCircle(20, 20, "red", 0.5)
makeCircle(30, 30, "blue", 0.5)
makeCircle(50, 50, "green", 0.9)
makeCircle(120, 20, "red", 1)
makeCircle(180, 30, "blue", 1)
makeCircle(150, 50, "green", 1)
<canvas id="canvas"></canvas>
Simple, right?
I just added a few more parameters to the function to pass the color and alpha.
Like that we can draw multiple color circles with ease.
You want to create some sort of animation using the globalAlpha...
We can use setInterval for that, here is an example:
function makeCircle(x, y, color, alpha) {
canvas.beginPath();
canvas.globalAlpha = alpha
canvas.arc(x, y, 20, 0, 2 * Math.PI);
canvas.fillStyle = color;
canvas.fill();
}
var globalAlpha = 0.1
function draw() {
globalAlpha += 0.02
if (globalAlpha > 2)
globalAlpha = 0.1
canvas.clearRect(0,0 , 999, 999)
makeCircle(20, 20, "red", globalAlpha)
makeCircle(30, 30, "blue", globalAlpha)
makeCircle(50, 50, "green", globalAlpha)
makeCircle(120, 20, "red", 1)
makeCircle(130, 30, "blue", 1)
makeCircle(150, 50, "green", 1)
}
var canvas_doc = document.getElementById("canvas");
var canvas = canvas_doc.getContext("2d");
setInterval(draw, 50)
<canvas id="canvas"></canvas>
There are a couple of things:
your canvas variable is not the canvas, it's the context. Rename it to avoid confusion (ctx is generally used).
clearInterval takes a parameter: a reference to the interval to clear. Put it in a variable, and use it.
you're not drawing anything inside your interval. You need to do ctx.fill() inside of it.
even with that change, you would only be drawing over the previously drawn circles (and not see any change). Once you drew something, the canvas is just a set of pixels. So if you want to retrieve part of what was behind the circle (by transparency), you need to clear your canvas and start over on every interval (redraw what was previously there before you added the circle). In the demo below, I save the image before drawing the first circle, and restore it on every iteration, but if other things are going on at the same time in your use case, you may actually want to redraw every element individually.
var canvas = document.querySelector('canvas'),
ctx = canvas.getContext("2d");
function makeCircle(x, y) {
// Save the background
var bg = ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, y, 20, 0, 2 * Math.PI);
color = '#f00'; // For the demo
ctx.fillStyle = color;
ctx.fill();
var timer = setInterval(function() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw the background
ctx.putImageData(bg, 0, 0);
if (ctx.globalAlpha >= .05) {
ctx.globalAlpha -= .05;
ctx.fill();
} else {
ctx.globalAlpha = 0;
clearInterval(timer);
}
}, 50);
}
ctx.fillRect(50, 50, 30, 100); // For the demo
makeCircle(50, 50);
<canvas></canvas>

layering and outlining on HTML canvas

I have been working on a seemingly simple graphic. I wish to create circles, with a line connecting the circles, and filling the circles in with some background. I have almost got it, but this one piece is tripping me up.
I can define the canvas, create the circles, and line connecting them just fine:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.width = $(window).width();
canvas.height = $(window).height();
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.lineWidth = 10;
//Create two nodes
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
//line connecting two nodes
ctx.moveTo(100, 100);
ctx.lineTo(200, 200);
ctx.stroke();
This would look like this:
What I then do is fill the circles with an image (this is why I use clip()), but using a white color fill for the sake of example demonstrates the problem as well:
//simulate filling in nodes with image, in this case solid color
ctx.clip();
ctx.fillStyle = "white";
ctx.fill();
Now I am almost there, but there are some jagged edges there that I have read is just a little "bug" in Chrome, and also I like that thick black outline on the circles. So, I want to go back over just the 2 circles and outline them. It seems no matter what I do, the context always remembers that line connecting the two, and I end up with the connector line over the top of the image after calling stroke():
//would like to just re-outline circles, not connecting line
ctx.stokeStyle = "black";
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
ctx.stroke();
What I can't figure out is how to just outline the 2 circles again after filling in the white background (loading the image)?
I think about it like drawing in layers. First I draw some lines, then I put the images in, then I draw again on top. Not sure if the html canvas is meant to be used like that. Thanks.
JSFiddle Example Here
You are forgetting to begin a new path.
Whenever you start a new shape you must use ctx.beginPath or the context will redraw all the previous paths.
BTW the jaggy circles is because you are re-rendering them, this causes the edges to get jaggies.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 500;
document.body.appendChild(canvas);
ctx.setTransform(1,0,0,1,0,-50); // just moving everything up to be seen in snippet.
ctx.beginPath();
ctx.strokeStyle = "black";
ctx.fillStyle = "#FAFAFF";
ctx.lineWidth = 10;
//Create two nodes
/* dont draw the two circle the first time as you are
doubling the render causing the edges to get to sharp
making them appear jaggy.
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
*/
//line connecting two nodes
ctx.moveTo(100, 100);
ctx.lineTo(200, 200);
ctx.stroke();
ctx.beginPath(); // start a new path and removes all the previous paths
//Create two nodes
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
ctx.fill();
ctx.beginPath(); // start a new path and removes all the previous paths
//Create two nodes
ctx.arc( 100, 100, 25, 0, 2*Math.PI);
ctx.moveTo(200+25, 200)
ctx.arc( 200, 200, 25, 0, 2*Math.PI);
ctx.stroke();

two radial gradient colours, combined when joined

I have a two radial gradient in canvas HTML5 and when I combine the two shapes, their colors combine, however, I want the colors to be separate, like in the following example:
http://jsfiddle.net/Kk2tY/
I need as a minimum the transparency of color similar to this :
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var rgba = new Array();
rgba[0] = new Array();
rgba[0][0] = [0, 255, 163, 0, 0.09];
rgba[0][1] = [0.3, 255, 168, 0, 0.07];
rgba[0][2] = [0.6, 255, 156, 0, 0.04];
rgba[0][3] = [1, 255, 169, 0, 0];
var x = 100,
y = 75,
// Radii of the white glow.
innerRadius = 5,
outerRadius = 70,
// Radius of the entire circle.
radius = 60;
ctx.beginPath();
var gradient = ctx.createRadialGradient(x, y, innerRadius, x, y, outerRadius);
gradient.addColorStop(0, "rgba(255,163,0,0.35)");
gradient.addColorStop(0.3, "rgba(255,168,0,0.24)");
gradient.addColorStop(0.6, "rgba(255,156,0,0.17)");
gradient.addColorStop(1, "rgba(255,169,0,0)");
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = gradient;
ctx.fill();
ctx.beginPath();
var gradient = ctx.createRadialGradient(x + 45, y, innerRadius, x + 45, y, outerRadius);
gradient.addColorStop(0, "rgba(255,163,0,0.35)");
gradient.addColorStop(0.3, "rgba(255,168,0,0.24)");
gradient.addColorStop(0.6, "rgba(255,156,0,0.17)");
gradient.addColorStop(1, "rgba(255,169,0,0)");
ctx.arc(x + 45, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = gradient;
ctx.fill();
http://jsfiddle.net/3Atnt/
I have also tried with context.globalAlpha but the result was the same.
When you use transparency, then all the different colors of a pixel will be added to get the final result.
If that's not the desired result, you're on your own.
What you need to do is draw a diagram (on paper) where you select various points and determine manually which color they should have.
Then look at all the points and try to come up with an algorithm that produces those results.
Finally, you need to convert that algorithm into JavaScript probably using a "pixel-by-pixel" rendering approach.
Another approach is to render the two shapes into two images and then use image processing to combine them according to certain rules. Google for html5 canvas image processing.

Problems with using fill() in a canvas - illogical behaviour

I'm trying to learn how to draw/fill different shapes by using canvas and JavaScript, but my shapes doesn't get filled in the way I want them to, at all. The body of my HTML-document is this simple line:
<canvas id="canvas1" width="500" height="500"></canvas>
And my JavaScript-file looks like this:
function draw() {
var canvas1 = document.getElementById('canvas1');
if(canvas1.getContext) {
var ctx = canvas1.getContext('2d');
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
gradient.addColorStop(0, "blue");
gradient.addColorStop(1, "white");
ctx.beginPath();
ctx.moveTo(25,25);
ctx.lineTo(100, 25);
ctx.stroke();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.moveTo(75, 100);
ctx.arc(50, 100, 25, 0, Math.PI*2, true);
ctx.fillStyle = "black";
ctx.fill();
ctx.beginPath();
ctx.fillStyle = gradient;
ctx.arc(75, 150, 25, 0, Math.PI*2, true);
ctx.fill();
}
}
But this is the result:
And I don't get it. I've tried filling my second circle with every other color, and that works just fine. Also if I remove the last "ctx.beginPath();" my first circle gets painted in gradient. But I can't get the same bug to work on my second circle by changing the position of the code or something. And every guide I've found tells me that this should work, as far as I understand it.
Gradients are defined with an absolute position so if you draw your circle outside the area defined by the gradient it will appear transparent instead of filled.
There is no need to close the path as the fill() method will close it implicit for you, but just make sure the coordinates in the gradient covers the area you want to fill.
Instead of calculating for each time you need to fill an arc you could create a generic wrapper function which takes a position and colors to fill (adjust as needed):
A demo here
/**
* Fills a circle with a two-color gradient.
* #param {Number} cx - center X
* #param {Number} cy - center Y
* #param {Number} radius - radius
* #param {String} col1 - start color as CSS color string
* #param {String} col2 - end color as CSS color string
* #param {Boolean} [horiz=false] - Set true for horizontal gradient
*/
function fillCircle(cx, cy, radius, col1, col2, horiz) {
var x = cx - radius,
y = cy - radius,
d = radius * 2,
gradient;
if (horiz) {
gradient = ctx.createLinearGradient(x, 0, x+d, d);
}
else {
gradient = ctx.createLinearGradient(0, y, 0, y+d);
}
gradient.addColorStop(0, col1);
gradient.addColorStop(1, col2);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, 2*Math.PI);
ctx.fill();
}
Then just use it this way:
fillCircle(200, 200, 70, 'yellow', 'red');
The last flag is optional here and makes a horizontal gradient if set to true.
Use ctx.closePath(); After each separate shape/line you want is done.
ctx.beginPath();
ctx.moveTo(25, 50);
ctx.bezierCurveTo(25, 50, 50, 80, 75, 60)
ctx.strokeStyle = "black";
ctx.stroke();
ctx.closePath();
The gradient needs to be set with the coordinates matching where your shape is on the canvas.
You have the gradient starting at 0,0,
var gradient = ctx.createLinearGradient(0, 0, 50, 0);
But your circle is locates at 25,50. Make your gradient coordinates the same as you circle coordinates.
http://jsfiddle.net/bC75t/1/

Mapping Color Space in HTML

So i'm trying to map color space in HTML and my knowledge is pretty much limited to CSS HTML and Javascript. I am looking for a way to construct a 2 dimensional gradient, with 2 variable along 2 vectors. My research has indicated that CSS and SVG tech only has capacity for single dimension grdaients. Or rather Linear Grads can only have a single vector. So to make up for this limitation I am using JS to iterate over the 256 changes I need so that I can get a gradient on 2 RGB color channels. So picture if you will an x-axis that is relative to for example purposes - Red and Grads from 0 to 255 and y-axis that is likewise relative - Green and Grads from 0 to 255 but with a JS iteration instead of a CSS linear-grad.
What I end up with is a beautiful representation of RGB color space !BUT! changes to the z-axis -blue channel in this example- means that I have to call on a JS function that iterates through 256 loops updating the background of 256 DOM elements with new CSS linear grads.
I am making this web-app because of the limitations that I see in current web-based color pickers a 256 step loop for each change of the Z-axis will place an unacceptable amount of computation overhead into the program.
Any Idea's for a better way to make a dual vector gradient? Perhaps I could make an app specific library for the HTML 5 canvas element??? Where I would be operating on a bitmap instead of DOM elements maybe significantly lower the processor cost-per-call?
You can use the canvas element for that. Here are some examples of colorpickers.
Basically you want to create two linear gradients, one horizontal one vertical, moving from transparent to whatever rgba colors you want. Then draw one gradient over the other on the canvas. There's kind of a catch though, I've found that canvas doesn't make very clean rgba gradients, but you can uses half transparent colors, draw the first one once, the second one twice, then the first one again and it seems to give pretty good results. You can play with it though, here's some code to work off of.
var Draw = function(clr1, clr2){
clr1 = clr1 || 'rgba(255, 0, 0, 0.5)';
clr2 = clr2 || 'rgba(0, 0, 255, 0.5)';
var bg1 = document.getElementById('canvas').getContext('2d'),
grad1 = bg1.createLinearGradient(0, 128, 256, 128),
grad2 = bg1.createLinearGradient(128, 0, 128, 256);
grad1.addColorStop(0, 'rgba(255, 0, 0, 0)');
grad1.addColorStop(1, clr1);
grad2.addColorStop(0, 'rgba(0, 0, 255, 0)');
grad2.addColorStop(1, clr2);
bg1.fillStyle = grad1;
bg1.fillRect(0, 0, 256, 256);
bg1.fillStyle = grad2;
bg1.fillRect(0, 0, 256, 256);
bg1.fillRect(0, 0, 256, 256);
bg1.fillStyle = grad1;
bg1.fillRect(0, 0, 256, 256);
}
Here's a simple example showing how to create an arbitrary gradient on a canvas, with per-pixel control: http://jsfiddle.net/j85FQ/3/
colorField( myCanvas, 500, 500, pretty );
function colorField(canvas,width,height,colorLookup){
var w = width-1, h = height-1;
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d'),
idata = ctx.getImageData(0,0,width,height),
data = idata.data;
for (var x=0;x<width;++x){
for (var y=0;y<height;++y){
var rgba = colorLookup(x/w,y/h);
var o = (width*y+x)*4;
for (var i=0;i<4;++i) data[o+i] = rgba[i]*255;
}
}
ctx.putImageData(idata,0,0);
}
function pretty(xPct,yPct){
return [ xPct, yPct, xPct*(1-yPct), 1];
}
Thanks guys I was able to work it out with the canvas element. I used a bucket fill for the z channel value and horizontal & vertical linear gradients from 0 to 255 for x and y channels. Setting context.globalCompositeOperation = "lighter" was the key I was missing. That was the simple additive mode I needed much easier then trying to find a suitable alpha compositing method. The following is the canvas init function I wrote.
function init() {
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
ctx.globalCompositeOperation = "lighter";
var grd = ctx.createLinearGradient(0, 0, 512, 0);
grd.addColorStop(0, "#000000");
grd.addColorStop(1, "#FF0000");
var grd2 = ctx.createLinearGradient(0, 0, 0, 512);
grd2.addColorStop(0, "#000000");
grd2.addColorStop(1, "#00FF00");
ctx.fillStyle = "#0000FF";
ctx.fillRect(0, 0, 512, 512);
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 512, 512);
ctx.fillStyle = grd2
ctx.fillRect(0, 0, 512, 512)
}

Categories