Fill in outline for HTML Canvas - javascript

I have an image that has holes in it and need to fill in the outline, here is the image for understanding, but I want to fill in the whole image red and leave the rest of the background white.
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.crossOrigin = 'anonymous';
img.src = "pinkclear.png";
var outline,
outlineCtx;
function draw(color)
{
ctx.clearRect(0,0,canvas.width,canvas.height);
// onload
if(typeof color !== 'string') color = 'red';
var cliparray = [];
var dArr = [-1,-1, 0,-1, 1,-1, -1,0, 1,0, -1,1, 0,1, 1,1], // offset array
s = 1, // scale
i = 1, // iterator
x = 5, // final position
y = 5;
// draw images at offsets from the array scaled by s
for(; i < dArr.length; i += 2)
{
ctx.drawImage(img, x + dArr[i]*s, y + dArr[i+1]*s);
// fill with color
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = color;
ctx.fillRect(0,0,canvas.width, canvas.height);
// keep only the outline
ctx.globalCompositeOperation = "destination-out";
ctx.drawImage(img, x, y);
// store the imageData in a new Canvas
outline = canvas.cloneNode(true);
outlineCtx = outline.getContext('2d')
//outlineCtx.drawImage(canvas,0,0);
// draw image in original mode
ctx.globalCompositeOperation = "source-over";
//ctx.drawImage(img, x, y);
}
}

Related

Image as a stencil for html canvas

I'm trying to have a user manually color in certain parts of an image. As an example, here's a cat https://techflourish.com/images/3-cat-clipart-9.png. The user should be able to color in the foot of the cat if they choose to. I want them to only color inside the cat body image portion of the canvas (not the background portion of the image or whitespace of canvas, but I guess I could just manually trim the image).
What I've attempted so far is below. Basically I check the color of the pixel at my position and draw only if it isn't that background color. This sort of works, but I'm able to bleed out really easily because something is off. I was wondering if it was possibly to set a specific clip area, but wasn't able to figure it out.
`
var canvas = document.getElementById("space");
var ctx = canvas.getContext("2d");
var pos = { x: 0, y: 0 };
// new position from mouse events
function setPosition(e) {
pos.x = e.clientX;
pos.y = e.clientY;
}
function rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
function draw(e) {
if (e.buttons !== 1) return; // if mouse is pressed.....
var color = "#cb3594";
ctx.beginPath(); // begin the drawing path
ctx.lineWidth = 5; // width of line
ctx.lineCap = "round"; // rounded end cap
ctx.strokeStyle = color; // hex color of line
var p = ctx.getImageData(pos.x, pos.y, 1, 1).data;
var sourceColor = rgbToHex(p[0], p[1], p[2]);
if(sourceColor != "BACKGROUNDHEX" && sourceColor != color) {
ctx.moveTo(pos.x, pos.y); // from position
setPosition(e);
p = ctx.getImageData(pos.x, pos.y, 1, 1).data;
targetColor = rgbToHex(p[0], p[1], p[2]);
if(targetColor != "BACKGROUNDHEX" && targetColor != color) {
ctx.lineTo(pos.x, pos.y); // to position
ctx.stroke(); // draw it!
}
}
}
var outlineImage = new Image();
outlineImage.onload = function() {
ctx.drawImage(outlineImage, 0, 0, 704, 720);
}
outlineImage.src = "IMAGE.png";
space.addEventListener("mousemove", draw);
space.addEventListener("mousedown", setPosition);
space.addEventListener("mouseenter", setPosition);
</script>
`
(related edit: the bleeding is caused by my "sourceColor != color" being wrong, but the question is still relevant as this still doesn't feel like a great solution)
Since the parts of the image you don't want to color are transparent, you can set the context's globalCompositeOperation to 'source-atop'. After that, any pixels you draw to the canvas will automatically take on the overwritten pixels' opacity, and you don't have to mess with getImageData:
var canvas = document.getElementById("space");
var ctx = canvas.getContext("2d");
var pos = {
x: 0,
y: 0
};
// new position from mouse events
function setPosition(e) {
// offsetX/Y gives the correct coordinates within the canvas
// assuming it has no padding
pos.x = e.offsetX;
pos.y = e.offsetY;
}
function draw(e) {
if (e.buttons !== 1) return; // if mouse is pressed.....
var color = "#cb3594";
ctx.beginPath(); // begin the drawing path
ctx.lineWidth = 5; // width of line
ctx.lineCap = "round"; // rounded end cap
ctx.strokeStyle = color; // hex color of line
ctx.moveTo(pos.x, pos.y); // from position
setPosition(e);
ctx.lineTo(pos.x, pos.y); // to position
ctx.stroke(); // draw it!
}
var outlineImage = new Image();
outlineImage.onload = function() {
// the default, set explicitly because we're changing it elsewhere
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(outlineImage, 0, 0);
// don't draw over the transparent parts of the canvas
ctx.globalCompositeOperation = 'source-atop';
// wait until the stencil is loaded before handing out crayons
space.addEventListener("mousemove", draw);
space.addEventListener("mousedown", setPosition);
space.addEventListener("mouseenter", setPosition);
}
outlineImage.src = "https://i.stack.imgur.com/so095.png";
<canvas id="space" width="610" height="733"></canvas>

Converting canvas to PNG returns blank image

I am experiencing an issue with converting a canvas to PNG.
Although the canvas looks exactly as I want it and the conversion from canvas to data URL PNG seems right, the image is blank.
I also tried converting a div to PNG but it did not work for me because I wanted a greyscale filter to be applied. Anyone have any ideas?
JavaScript
var imgis = new Image();
var bubble = new Image();
var canvasWidth;
var canvasHeight;
var ctx = canvas.getContext('2d');
bubble.onload = function() {
var imgis = new Image();
var bubble = new Image();
var ctx = canvas.getContext('2d');
bubble.onload = function() {
// set the canvas' size
canvas.width = this.width;
canvas.height = this.height;
// first fill a rect
ctx.fillStyle = 'rgba(255, 255, 255, 0)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// set the gCO
ctx.globalCompositeOperation = 'luminosity';
// if the browser doesn't support Blend Modes
console.log(ctx.globalCompositeOperation)
if (ctx.globalCompositeOperation !== 'luminosity')
fallback(this);
else {
// draw the image
ctx.drawImage(this, 0, 0);
ctx.drawImage(imgis, 30, 60);
// reset the gCO
ctx.globalCompositeOperation = 'source-over';
}
}
imgis.crossOrigin = "anonymous";
bubble.crossOrigin = "anonymous";
imgis.src = "image1 src";
bubble.src = "image2 src";
function fallback(img) {
// first remove our black rectangle
ctx.clearRect(0, 0, canvas.width, canvas.height);
//draw the image
ctx.drawImage(img, 0, 0);
ctx.drawImage(imgis, 30, 60);
// get the image data
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var d = imgData.data;
// loop through all pixels
// each pixel is decomposed in its 4 rgba values
for (var i = 0; i < d.length; i += 4) {
// get the medium of the 3 first values
var med = (d[i] + d[i + 1] + d[i + 2]) / 3;
// set it to each value
d[i] = d[i + 1] = d[i + 2] = med;
}
// redraw the new computed image
ctx.putImageData(imgData, 0, 0);
}
canvas = document.getElementById('canvas');
var image = Canvas2Image.convertToPNG(canvas);
console.log(image.src);
// document.getElementById('theDemo').src = image.src;
var image_data = $(image).attr('src');
console.log(image_data);
$("#theDemo").attr('src', image_data);
HTML
<canvas id='canvas' > </canvas>
<img src="" id="theDemo" />
I assume you're using canvas2image. You should replace var image = Canvas2Image.convertToPNG(canvas); with Canvas2Image.convertToPNG(canvas, width, height). Hopefully that helps!
EDIT Since the issue is with the actual canvas to base64 conversion, you can try to use the .toDataURL() method instead of using that library. My comment explains how to test this in your specific code.

Feather Boarders of Canvas Image

i have a canvas with the following images loaded:
Background Image:
Front Image:
Both together looks like this image:
Now i want to apply a feather effect to the hand boarders to result in to something like this:
I tried the following solution so far. But the result is not like on the above image.
var temp = document.createElement('canvas'),
tx = temp.getContext('2d');
temp.width = scope.canvas.width;
temp.height = scope.canvas.height;
tx.translate(-temp.width, 0);
tx.shadowOffsetX = temp.width;
tx.shadowOffsetY = 0;
tx.shadowColor = '#000';
tx.shadowBlur = 100;
var img = new Image();
img.onload = function() {
tx.drawImage(img, 0, 0, 512, 512);
};
img.src = imageURL; // the hand image
var temp2 = document.createElement('canvas'),
tx2 = temp2.getContext('2d');
temp2.width = scope.canvas.width;
temp2.height = scope.canvas.height;
var img2 = new Image();
img2.onload = function() {
tx2.drawImage(img2, 0, 0, 512, 512);
tx2.save();
tx2.globalCompositeOperation = 'destination-out';
tx2.drawImage(temp, 0, 0);
tx2.restore();
};
img2.src = temp.toDataURL("image/png");
Any idea how to solve this?
Regards
Steve
To feather an image, create a copy of it via a new canvas, create an invers mask of the image with destination-out comp. Then draw the hand again and then mask with destination-ou but with a shadow to create the feather.
var canvas = document.createElement("canvas");
canvas.width = 1024,canvas.height = 1024;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
var hand = new Image();
hand .src = "http://i.stack.imgur.com/PbAfc.png";
hand .onload = function(){
var can = document.createElement("canvas");
can.width = this.width;
can.height = this.height;
ct = can.getContext("2d");
// create inverted mask
ct.fillStyle = "black";
ct.fillRect(0,0,this.width,this.height);
ct.globalCompositeOperation = "destination-out";
ct.drawImage(this,0,0);
// create second canvas
var can1 = document.createElement("canvas");
can1.width = this.width;
can1.height = this.height;
ct1 = can1.getContext("2d");
// draw image
ct1.drawImage(this,0,0);
ct1.shadowColor = "black";
ct1.shadowBlur = 30; // amount of feather
ct1.globalCompositeOperation = "destination-out";
ct1.drawImage(can,0,0);
ct1.shadowBlur = 20; // amount of feather
ct1.drawImage(can,0,0); // The mor you draw the greater the FX
ct1.shadowBlur = 10; // amount of feather
ct1.drawImage(can,0,0); // The mor you draw the greater the FX
ct1.globalCompositeOperation = "source-over";
ct.globalCompositeOperation = "source-over";
ctx.drawImage(can1,0,0);
// use the new canvas can1 as the hand image in later code.
}
//ctx.fillStyle = "#e19e9e"
ctx.fillRect(0,0,1024,1024);

Image filling in polygon using Raphael js or any another js

I am facing the issue on filling the background image within the polygon selected at roof top.
I have successfully created the polygon, now I want that as soon as the image is selected from the number of slides present, that image should get filled within the selected polygon.
I am using Raphael js for doing the same, if possible with any other js then please advise.
Below is the code for testing purpose:
// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);
//draw triangle
var t = paper.path("M0 0L250 0L100 100L 0");
// Sets the fill attribute of the circle to red (#f00)
t.attr("fill", "url('http://interlock.renoworks.com/en/data/exterior/Slate/~Interlock-01-SlateRoofing/~swatch1-400.jpg')");
// Sets the stroke attribute of the circle to white
t.attr("stroke", "#f00");
Here is the demo url: http://jsfiddle.net/hxez863d/5/
You can clip a path in HTML5 without a library. Draw on the canvas after to put things in the clip region.
var can = document.getElementById('can');
var ctx = can.getContext('2d');
var noise = makeNoise(300,200);
var squares = makeSquares(300, 200, 10, "#CCCCCC", "#999999");
// Draw background image.
ctx.drawImage(noise, 0, 0);
ctx.save();
//var paper = Raphael(10, 50, 320, 200);
ctx.translate(10, 50);
ctx.save();
//draw triangle
//var t = paper.path("M0 0L250 0L100 100L 0");
clipPath(ctx, [
[0,0],
[250, 0],
[100, 100],
[0, 0]
]);
// Draw with clip.
ctx.drawImage(squares, 0, 0);
ctx.fillRect(90, 70, 30, 30);
ctx.restore(); // <-- removes clip
// Draw without clip.
ctx.fillStyle = "red";
ctx.fillRect(100, 80, 30, 30);
ctx.restore(); // <-- removes translate
function clipPath(ctx, coords) {
ctx.beginPath();
ctx.moveTo(coords[0][0], coords[0][1]);
for (var i = 1; i < coords.length; i++) {
ctx.lineTo(coords[i][0], coords[i][1]);
}
ctx.clip();
}
function makeNoise(w, h) {
var can = document.createElement('canvas');
can.width = w;
can.height = h;
var ctx = can.getContext('2d');
var imageData = ctx.getImageData(0, 0, w, h);
var d = imageData.data;
var len = d.length;
for (var i = 0; i < len; i+=4) {
d[i] = d[i+1] = d[i+2] = Math.floor(Math.random() * 256);
d[i+3] = 255;
}
ctx.putImageData(imageData, 0, 0);
return can;
}
function makeSquares(w, h, size, color1, color2) {
var can = document.createElement('canvas');
var ctx = can.getContext('2d');
can.width = w;
can.height = h;
for (var y = 0; y < h; y+= size) {
for (var x = 0; x < w; x += size*2) {
ctx.fillStyle = color1;
ctx.fillRect(x, y, size, size);
ctx.fillStyle = color2;
ctx.fillRect(x + size, y, size, size);
}
var temp = color1;
color1 = color2;
color2 = temp;
}
return can;
}
<canvas id="can" width="300" height="200"></canvas>

Re-fill area of erased area for canvas

I am trying to fill color in image using below code snippet for filling color on Image of canvas . Its successfully filling color in canvas. Now I am trying to erase filled color on touch of user using this code snippet for erasing color on Image of canvas . Its erasing color & setting transparent area on that touched position. Now I want to refill that area on user touch with colors but its not allowing me to color on that because of transparent pixels. So Is there any way to refill pixels with color Or Is there any other way to erase color from image of canvas ? Any reply will be appreciated.
code snippet for filling color on Image of canvas
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.crossOrigin = 'anonymous';
img.src = "https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png";
function draw(color) {
ctx.drawImage(img, 0, 0);
}
canvas.onclick = function(e){
var rect = canvas.getBoundingClientRect();
var x = e.clientX-rect.left,
y = e.clientY-rect.top;
ctx.globalCompositeOperation = 'source-atop';
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(x-5,y-5,10,0,2*Math.PI);
ctx.fill();
}
code snippet for erasing color on Image of canvas
(function() {
// Creates a new canvas element and appends it as a child
// to the parent element, and returns the reference to
// the newly created canvas element
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(container, width, height, fillColor) {
var canvas = createCanvas(container, width, height);
var ctx = canvas.context;
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
ctx.clearTo = function(fillColor) {
ctx.fillStyle = fillColor;
ctx.fillRect(0, 0, width, height);
};
ctx.clearTo(fillColor || "#ddd");
// bind mouse events
canvas.node.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var radius = 10; // or whatever
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
canvas.node.onmousedown = function(e) {
canvas.isDrawing = true;
};
canvas.node.onmouseup = function(e) {
canvas.isDrawing = false;
};
}
var container = document.getElementById('canvas');
init(container, 531, 438, '#ddd');
})();
Warning untested code!
// create a clipping region using your erasing rect's x,y,width,height
context.save();
context.beginPath();
context.rect(erasingRectX,erasingRectY,erasingRectWidth,erasingRectHeight);
context.clip();
// redraw the original image.
// the image will be redrawn only into the erasing rects boundary
context.drawImage(yourImage,0,0);
// compositing: new pixels draw only where overlapping existing pixels
context.globalCompositeOperation='source-in';
// fill with your new color
// only the existing (clipped redrawn image) pixels will be colored
context.fillStyle='red';
context.fillRect(0,0,canvas.width,canvas.height);
// undo the clipping region
context.restore();

Categories

Resources