I want to mask the original image using alpha mask image. I can do alpha masking on image with the following code. But I want to be able to edit the mask with the brush, how can I do that?
In theory if I paint the mask white it should be opaque, if I paint it black it should be transparent.
The code I tried:
window.onload = function () {
var img = document.getElementById("mask");
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data32 = new Uint32Array(idata.data.buffer);
var i = 0, len = data32.length;
while (i < len) data32[i] = data32[i++] << 8;
ctx.putImageData(idata, 0, 0);
ctx.globalCompositeOperation = "source-in";
const defaultImg = document.getElementById('img');
ctx.drawImage(defaultImg, 0, 0);
};
.container {
text-align: center;
}
<div class="container">
<p>Default Image </p>
<img id="img" crossorigin="anonymous" src="https://i.ibb.co/FhgZgzN/cat.png">
<p>Mask </p>
<img id="mask" crossorigin="anonymous" src="https://i.ibb.co/NswxsLc/cat-mask.png">
<p>Result </p>:
<canvas id="canvas"></canvas>
</div>
You can use FabricJS to enable mask drawing. I've done this in the MockoFun graphic designer.
There's a discussion on Github about this: https://github.com/fabricjs/fabric.js/issues/6465#issuecomment-1127690007
Create a new brush that extends the PencilBrush (https://github.com/fabricjs/fabric.js/blob/master/src/brushes/pencil_brush.class.js)
Add 2 options for this brush:
targetMaskFilter - to store the reference to the BlendImage filter
mode that is source-over or destination-over to remove/add from the mask image
The idea is to draw on the mask layer using the brush and then combining the mask with the original image using the BlendImage filter.
Here's a Gist showing my implementation: https://gist.github.com/codingdudecom/ba183221d705a23962fcfcd3cae0c63f
Related
I want to take a screenshot of the following canvas:
<div class="A">
<canvas width="700" height="500" style="filter: brightness(200%)"></canvas>
<div class="A">
I have the following method:
takeSnapshot() {
const canvas = document.getElementsByClassName('A')[0].childNodes[0] as HTMLCanvasElement;
console.log(canvas);
const dataUrl = canvas.toDataURL('png');
const link = document.getElementsByClassName('link')[0];
link.setAttribute('download', 'snapshot-' + this.convertTimeStamp(Date.now()) + '.png');
link.setAttribute('href', dataUrl.replace('image/png', 'image/octet-stream'));
link.click();
}
But the style style="filter: brightness(200%)" is not keepling on the png dowloaded image. How can i do?
Filter canvas pixels
The filter applied by the canvas style does not effect the pixels held by the canvas. This style is applied while compositing the canvas with the rest of the page.
To change the pixels on the canvas you need to set CanvasRenderingContext2D filter property and then render what you want the filter applied to.
Note that filter is experimental so check support.
Note that some filters will taint the canvas and thus not let you access the pixels, toDataURL will throw an error and you will not be able to download the canvas.
This does not apply to the standard named CSS filters like functions blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), saturate(), and sepia()
Example
Applying brightness filter before downloading canvas content.
takeSnapshot() {
const canvas = document.getElementsByClassName('A')[0].childNodes[0];
const ctx = canvas.getContext("2d");
// Set important canvas state to defaults
ctx.globalAlpha = 1;
ctx.setTransform(1, 0, 0, 1, 0, 0);
// Set the filter function
ctx.filter = "brightness(200%)";
// Make sure all pixels are completely replaced. Important if there are transparent pixels
ctx.globalCompositeOperation = "copy";
// Copy canvas to itself applying the filter as it does so.
ctx.drawImage(canvas, 0, 0);
// Turn off filter
ctx.filter = "none";
// Restore default comp op
ctx.globalCompositeOperation = "source-over";
// Download
const link = document.getElementsByClassName('link')[0];
link.download = 'snapshot-' + this.convertTimeStamp(Date.now()) + '.png';
link.href = canvas.toDataURL('png').replace('image/png', 'image/octet-stream');
link.click();
}
Note This requires the CanvasRenderingContext2D. If the canvas content was created using another API you will not be able to get a 2d context. You thus need to create a temp canvas, the same size and draw the old canvas onto the new one (using the same method as in the example above) then download the new canvas.
If canvas.getContext("2d") returns null
As I don't know how your canvas was rendered the more robust version of the function checks to see if a 2D context can be obtained, if not then copy (using filter) to a new canvas and save that.
takeSnapshot() {
var dlCanvas, ctx;
const can = document.getElementsByClassName('A')[0].childNodes[0];
ctx = can.getContext("2d"); // ctx may be null (eg the canvas was rendered using webGL
if (!ctx) { // failed to get 2D API create a second canvas to apply the filter
dlCanvas = Object.assign(document.createElement("canvas"), {
width: can.width, height: can.height
});
ctx = dlCanvas.getContext("2d");
} else {
dlCanvas = can;
}
ctx.globalAlpha = 1;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.filter = "brightness(200%)";
ctx.globalCompositeOperation = "copy";
ctx.drawImage(can, 0, 0);
ctx.filter = "none";
ctx.globalCompositeOperation = "source-over";
const link = document.getElementsByClassName('link')[0];
link.download = 'snapshot-' + this.convertTimeStamp(Date.now()) + '.png';
link.href = dlCanvas.toDataURL('png').replace('image/png', 'image/octet-stream');
link.click();
}
I have an image which is loaded from server and contains 4 bands in RGB format. Because of the alpha band which causes the image to look transparently, then I need to crop this alpha band and save it as JPEG format. One way I can think is using canvas, however, I'm not sure it possible or not as the test I did show that the image is the same.
<p>Image to use:</p>
<img id="scream" src="data/with_alpha.png" alt="The Scream">
<p>Canvas:</p>
<canvas id="myCanvas" width="2400" height="2097" style="border:1px solid #d3d3d3;">
</canvas>
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.globalAlpha = 1;
var img = document.getElementById("scream");
ctx.drawImage(img, 0, 0);
// This ones does not do anything to remove the alpha band
c.toDataURL("image/jpeg", 0.0);
}
I found the answer from http://sunbox.github.io/image-to-canvas-to-image/ it can be like this to convert canvas to image:
// Get the image
var sampleImage = document.getElementById("ringoImage"),
canvas = convertImageToCanvas(sampleImage),
image = convertCanvasToImage(canvas);
// Actions
document.getElementById("pngHolder").appendChild(image);
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
}
// Converts canvas to an image
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/jpeg");
return image;
}
I am trying to allow users to upload a picture, if it is too big I resize it using a canvas then I want to save that canvas as their picture. This is my code:
<input type="file" id="Avatar" class="input-file" name="uploadProfile" accept="image/*" onchange="myFunction()" />
<canvas id="BigPicture" style="border:1px solid #d3d3d3;"/>
<script>
function myFunction() {
//get the element with the picture
var x = document.getElementById(name["Avatar"]);
var size = x.files[0].size;
//if size is too big resize
if (size >= 4000000) {
//when the image loads
img.onload = function () {
//this is the size to reduce the picture by
var resize = .5;
//Get the content of the canvas in a 2d model
var ctx = c.getContext("2d");
/// step 1 - draw the picture to a dummy canvas so we can modify it before uploading
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
//make the size of the dummy canvas
oc.width = img.width * resize;
oc.height = img.height * resize;
octx.drawImage(img, 0, 0, oc.width, oc.height);
// the two step reduction of the picture is totry to preserve picture quality.
//Does not work for really large pictures as they end up looking pixleated.
/// step 2 - resize the image
octx.drawImage(oc, 0, 0, oc.width * resize, oc.height * resize);
var c = document.getElementById("BigPicture");
var ctx = c.getContext("2d");
do{
/// step 3, resize image until it is small enough
ctx.drawImage(oc, 0, 0, oc.width * resize, oc.height * resize, 0, 0, c.width, c.height);
} while (imgData.data.length >= 4000000)
}
}
</script>
(BTW I copied snippets of code from my work ^^ this all works for me.)
So once I have loaded the picture and resized it then displayed it for the user to see I want to take the canvas image and replace the input info with the canvas image.
like so...
<script>
function ifUserLikes()
{
var c = document.getElementById("BigPicture");
var ctx = c.getContext("2d");
$("Avatar").val(c);
}
</script>
This question already has an answer here:
HTML Canvas: Drawing grid below a plot
(1 answer)
Closed 6 years ago.
I have a canvas, and I want to use drawImage to draw an image behind the current content on the canvas.
Due to the fact that there is content already on the canvas (I'm using Literally Canvas to create a canvas containing an image, so I can't really draw the image first), I cannot use drawImage before I render the rest of my content.
Is it possible to drawImage behind all other content on a canvas?
Yes you can just use globalCompositeOperation destination-over, but note that your first image needs some transparency, otherwise, you will obviously not see anything :
var img1 = new Image();
var img2 = new Image();
var loaded = 0;
var imageLoad = function(){
if(++loaded == 2){
draw();
}
};
img1.onload = img2.onload = imageLoad;
var draw = function(){
var ctx = c.getContext('2d');
ctx.drawImage(img1, 100,100);
// wait a little bit before drawing the background image
setTimeout(function(){
ctx.globalCompositeOperation = 'destination-over';
ctx.drawImage(img2, 0,0);
}, 500);
}
img1.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png";
img2.src = "https://picsum.photos/200/200";
<canvas id="c" width="200" height="200"></canvas>
Sorry about the previous post, I didn't properly read your post
Perhaps you could save the canvas, draw your image, and then reload the old content on top of your drawn image? Here's some JS psuedocode:
var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
ctx.drawImage('Your Image Watermark Stuff');
ctx.putImageData(imgData,0,0);
You can use KonvaJS. And then use layers for it.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Rect Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var baseImage = new Konva.Image({
x: 50,
y: 50,
width: width,
height: height,
image: image
});
// add the shape to the layer
layer.add(rect);
// add the layer to the stage
stage.add(layer);
};
imageObj.src = 'url to your image'
</script>
</body>
</html>
A simple solution would be to use another canvas behind the first one.
Normally canvas pixels are initialized to transparent black and therefore are perfectly see-through.
If your first canvas is created opaque instead the only other option I can think to is
create a temporary canvas of the same size
draw your image in this temporary canvas
get the ImageData object of both the temporary canvas and of the original canvas
copy from the temporary canvas to the original canvas only where the original canvas is not set at the background color
In code:
var tmpcanvas = document.createElement("canvas");
tmpcanvas.width = canvas.width;
tmpcanvas.height = canvas.height;
var temp_ctx = tmpcanvas.getContext("2d");
// ... draw your image into temporary context ...
var temp_idata = temp_ctx.getImageData(0, 0, canvas.width, canvas.height);
var temp_data = temp_idata.data;
// Access the original canvas pixels
var ctx = canvas.getContext("2d");
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = idata.data;
// Find the background color (here I'll use first top-left pixel)
var br_r = data[0], bg_g = data[1], bg_b = data[2];
// Replace all background pixels with pixels from temp image
for (var i=0,n=canvas.width*canvas.height*4; i<n; i+=4) {
if (data[i] == bg_r && data[i+1] == bg_g && data[i+2] == bg_b) {
data[i] = tmp_data[i];
data[i+1] = tmp_data[i+1];
data[i+2] = tmp_data[i+2];
data[i+3] = tmp_data[i+3];
}
}
// Update the canvas
ctx.putImageData(idata, 0, 0);
this approach however will have a lower quality if the original canvas graphics has been drawn with antialiasing or if pixels of the background color are also used in the image (e.g. an object on #FFF white background where object highlights are also #FFF). Another problem is if the background color is not a perfectly uniform RGB value (this will happen if the image has been compressed with a lossy algorithm like jpeg).
All these problems could be mitigated with more sophisticated algorithms like range matching, morphological adjustments and color-to-alpha conversions (basically the same machinery used for chroma-keying).
I'm looking for a way to overlay the visible areas of a transparent black silhouette PNG file with a certain pattern.
Is there a CSS, JavaScript, mixed solution for this?
Example: I have one image of a weapon silhoutte and a set of camo patterns which I want to lay over the weapon.
Demo: In Photoshop the same result is givin when selecting layer mask > overlay.
Ps: my question is similar to this thread: Silhouette a PNG image using CSS except I am looking for the exact opposite.
You can do this using canvas with globalCompositeOperation set to destination-in. For example
var canvas = document.createElement('canvas');
canvas.width = 250;
canvas.height = 250;
var canvas_context = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
var msk = new Image();
msk.onload = function(){
canvas_context.drawImage(img, 0, 0);
canvas_context.globalCompositeOperation = "destination-in";
canvas_context.drawImage(msk, 0, 0);
canvas_context.globalCompositeOperation = "source-over";
};
msk.src = 'silhouette.png';
}
img.src = 'pattern.jpg';
document.body.appendChild(canvas);