I have an imageCanvas with it's Transform matrix
Image = new image();
image.src = //stuff;
imageContext.drawImage(Image, 0, 0);
var t = imageContext.currentTransform;
that is drawn on another canvas
context.drawImage(imageCanvas, 0, 0);
on which I draw lines
context.moveTo(mousePos.X, mousePos.Y);
context.lineTo(currentPos.X, currentPos.Y);
context.stroke();
I save my lines into a vector of objects 'lines'.
I would like to join the two layers into the 'Image', but this function works only for traslation but not for rotation and scaling.
var saveCanvas = document.createElement('canvas');
var saveContext = saveCanvas.getContext('2d');
saveCanvas.width = Image.width;
saveCanvas.height = Image.height;
saveContext.drawImage(Image,0,0);
saveContext.save();
var t = imageContext.currentTransform;
saveContext.setTransform(1,0,0,1,-t.e,-t.f);
for(var i = 0; i < lines.length; i++){
saveContext.beginPath();
saveContext.moveTo(lines[i].from.X, lines[i].from.Y);
saveContext.lineTo(lines[i].to.X, lines[i].to.Y);
saveContext.stroke();
}
saveContext.restore();
Image.src = saveCanvas.toDataURL();
how can I modify the arguments of 'setTransform' to solve the problem ?
Just use the rest of the transforms attributes.
var t = imageContext.currentTransform;
saveContext.setTransform(
t.a,
t.b,
t.c,
t.d,
-t.e,
-t.f);
Related
I get an issue with my javascript code... I'd like to display 5 lines of different pictures in my canvas but only one line is displayed and the LIFE function is only called 16 times (canvas.width / imgW).
Why the LIFE function can't be called after the 'while' loop ?
Thank you !
This is the result i get
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var tabImages = ['css3.svg', 'adobe-1.svg', 'html-5.svg', 'jquery.svg'];
var canvasW = canvas.width;
var canvasH = canvas.height;
var imgW = 50;
var imgH = 50;
var x = 0, y = 0, i = 0, posY = 1, increImg = 0, posX = 0;
/* I want to display 5 lines of pictures but only one is displayed */
for(var lignes = 0 ; lignes < 5 ; lignes++) {
i = 0;
while(posX < canvasW) {
/* That function is only called 16 times but i know only that
way to display several differentes images in canvas */
(function(posX, posY) {
var img = new Image();
if(increImg === tabImages.length-1)
increImg = 0;
img.src = 'testanim/img/'+tabImages[increImg];
img.onload = function() {
ctx.drawImage(img, posX, posY, imgW, imgH);
}
})(posX, posY)
i++;
increImg++;
posX = imgW * i;
}
posY = lignes * imgH;
}
</script>
are you looking for something like à kind of gallery :
//our images
let imageArr = ["animage","another","another","another","another","another","another","another","another","another"];
//getting the context (mycanvas is the id of the canvas element)
let context = mycanvas.getContext("2d")
//checking the context
if(context)
// for all images in my array(can't do for in because of the calculation of position later on )
for(let i = 0;i<imageArr.length;i++)
{
//creation of an image html element
let imgel = document.createElement('img');
//addition of src tag
imgel.src=imageArr[i];
//drawing of the image (parameters:(image, xcoordinates,ycoordinates,width,height)
context.drawImage(imgel, (i%5)*100, (Math.floor(i/5))*100,100,100);
//drawing a rectangle around them
context.strokeRect((i%5)*100,(Math.floor(i/5))*100,100,100);
}
canvas{border:1px solid black;}
<canvas id="mycanvas" height="500" width="500"></canvas>
so in order to have several lines of your image in your code you have to use % operator:
for instance if you want 5 lines this gives you :
(i%canvasW)*imgW
what this does it simply stops after 5 images and if you have to go to the line(see below) it will start from 0 again : 0%5 = 0 1%5 = 1 , ... , 5%5 = 0 6%5 = 1
On the other side for the height if you want to go to the line you need to do some kind of oposite calculation which is / this gives you :
Math.floor(i/5)*imgH
This gives you the lines you are hoping for
so if i resume you will have :
ctx.drawImage(img, (i%canvasW)*imgW, Math.floor(i/5)*imgH, imgW, imgH);
and that's also why your while loop is not going through several lines, you have to calculate your posX differently like so :
posX = (i%canvasW)*imgW;
and throw in another variable for the height which will be the condition to stop your while loop
for instance let's say you want to stop once the last image of the last line is hit you would have to calculate this condition like so :
if(canv.getContext("2d"))
{
let max = canv.width*canv.height;
let current = 0;
let imgW=50;
let imgH=50;
let i = 0;
while(current<=max)
{
console.log("stuff");
current = (Math.floor(i/canv.height)*imgH)*((i%canv.width)*imgW);
i++;
}
}
<canvas id="canv" width="500" height="500"></canvas>
i strongly advise using for loops since you almost can calculate the number of steps for every problem :
for(let i = 0; i<(canvas.width*canvas.height);i++)
I am working on a small app, that loads user image onto a server, lets him choose one of the filters and gives image back.
I need to somehow save the initial image data with no filters applied.
But as i found out, in JS there is no natural way to copy vars.
I tried using LoDash _.clone() and one of the jQuery functions to do this, but they didn't work.
When I applied a cloned data to image, function putImageData couldn't get the cloned data because of the wrong type.
It seems, that clone functions somehow ignore object types.
Code:
var img = document.getElementById("image");
var canvas = document.getElementById("imageCanvas");
var downloadLink = document.getElementById("download");
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, img.width, img.height);
document.getElementById("image").remove();
initialImageData = context.getImageData(0, 0, canvas.width, canvas.height); //initialImageData stores a reference to data, but I need a copy
///////////////////////
normalBtn.onclick = function(){
if(!(currentState == converterStates.normal)){
currentState = converterStates.normal;
//here I need to apply cloned normal data
}
};
So, what can I do here???
Thanks!!!
The correct way to copy a typed array is via the static function from
eg
var imageData = ctx.getImageData(0,0,100,100);
var copyOfData = Uint8ClampedArray.from(imageData.data); // create a Uint8ClampedArray copy of imageData.data
It will also allow you to convert the type
var copyAs16Bit = Uint16Array.from(imageData.data); // Adds high byte. 0xff becomes 0x00ff
Note that when converting to a smaller type the extra bits are truncated for integers. When converting from floats the value not the bits are copied. When copying between signed and unsigned ints the bits are copied eg Uint8Array to Int8Array will convert 255 to -1. When converting from small int to larger uint eg Int8Array to Uint32Array will add on bits -1 becomes 0xffff
You can also add optional map function
// make a copy with aplha set to half.
var copyTrans = Uint8ClampedArray.from(imageData.data, (d, i) => i % 4 === 3 ? d >> 1 : d);
typedArray.from will create a copy of any array like or iterable objects.
Use :
var image = …;
var data = JSON.parse(JSON.stringify(image).data);
var arr = new Uint8ClampedArray(data);
var copy = new ImageData(arr, image.width, image.height);
An ImageData object holds an Uint8ClampedArray which itself holds an ArrayBuffer.
To clone this ArrayBuffer, you can use its slice method, or the one from the TypedArray View you get :
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(0,0,300,150);
var original = ctx.getImageData(0,0,300,150);
var copiedData = original.data.slice();
var copied = new ImageData(copiedData, original.width, original.height);
// now both hold the same values
console.log(original.data[25], copied.data[25]);
// but can be modified independently
copied.data[25] = 0;
console.log(original.data[25], copied.data[25]);
<canvas id="canvas"></canvas>
But in your case, an easier solution, is to call twice ctx.getImageData.
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(0,0,300,150);
var original = ctx.getImageData(0,0,300,150);
var copied = ctx.getImageData(0,0,300,150);
// both hold the same values
console.log(original.data[25], copied.data[25]);
// and can be modified independently
copied.data[25] = 0;
console.log(original.data[25], copied.data[25]);
<canvas id="canvas"></canvas>
And an complete example :
var ctx = canvas.getContext('2d');
var img = new Image();
// keep these variables globally accessible to our script
var initialImageData, filterImageData;
var current = 0; // just to be able to switch easily
img.onload = function(){
// prepare our initial state
canvas.width = img.width/2;
canvas.height = img.height/2;
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// this is the state we want to save
initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
// get an other, independent, copy of the current state
filterImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
// now we can modify one of these copies
applyFilter(filterImageData);
button.onclick = switchImageData;
switchImageData();
}
// remove red channel
function applyFilter(image){
var d = image.data;
for(var i = 0; i < d.byteLength; i+=4){
d[i] = 0;
}
}
function switchImageData(){
// use either the original one or the filtered one
var currentImageData = (current = +!current) ?
filterImageData : initialImageData;
ctx.putImageData(currentImageData, 0, 0);
log.textContent = current ? 'filtered' : 'original';
}
img.crossOrigin = 'anonymous';
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
<button id="button">switch imageData</button>
<code id="log"></code><br>
<canvas id="canvas"></canvas>
The same with slice:
var ctx = canvas.getContext('2d');
var img = new Image();
// keep these variables globally accessible to our script
var initialImageData, filterImageData;
var current = 0; // just to be able to switch easily
img.onload = function(){
// prepare our initial state
canvas.width = img.width/2;
canvas.height = img.height/2;
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// this is the state we want to save
initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
// get an other, independent, copy of the current state
filterImageData = new ImageData(initialImageData.data.slice(), initialImageData.width, initialImageData.height);
// now we can modify one of these copies
applyFilter(filterImageData);
button.onclick = switchImageData;
switchImageData();
}
// remove red channel
function applyFilter(image){
var d = image.data;
for(var i = 0; i < d.byteLength; i+=4){
d[i] = 0;
}
}
function switchImageData(){
// use either the original one or the filtered one
var currentImageData = (current = +!current) ?
filterImageData : initialImageData;
ctx.putImageData(currentImageData, 0, 0);
log.textContent = current ? 'filtered' : 'original';
}
img.crossOrigin = 'anonymous';
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
<button id="button">switch imageData</button>
<code id="log"></code><br>
<canvas id="canvas"></canvas>
Okay, I am confused with get Image Data function for
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData
I have a path image that is in png format with transparent background above. What I need to get are the coordinates x,y for both left and right edges of the path at height/2. (The points of the red arrows)
Is getImageData the right function to use? Can anyone give some advice on how to get them?
Thanks in advance.
Yes, use getImageData(x, y, width, height);
In the case you have only two colors (here transparent & white) :
var img = new Image();
img.onload = getPoints;
img.crossOrigin = 'anonymous';
img.src = "https://dl.dropboxusercontent.com/s/lvgvekzkyqkypos/path.png";
function getPoints(){
// set our canvas
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
// draw the image
ctx.drawImage(this,0,0);
// get the middle y line
var imageData = ctx.getImageData(0,Math.floor(this.height/2), this.width, 1);
var data = imageData.data;
// set an empty object that will store our points
var pts = {x1: null, y1: Math.floor(this.height/2), x2: null, y2:Math.floor(this.height/2)};
// define the first opacity value
var color = 0;
// iterate through the dataArray
for(var i=0; i<data.length; i+=4){
// since we'relooking only for non-transparent pixels, we can check only for the 4th value of each pixel
if(data[i+3]!==color){
// set our value to this new one
color = data[i+3];
if(!pts.x1)
pts.x1 = i/4;
else
pts.x2 = i/4;
}
}
snippet.log('start points : '+pts.x1+'|'+pts.y1);
snippet.log('end points : '+pts.x2+'|'+pts.y2);
ctx.fillStyle = 'red';
ctx.fillRect(pts.x1-5, pts.y1, 10, 10);
ctx.fillRect(pts.x2-5, pts.y2, 10, 10);
document.body.appendChild(canvas);
}
body{background-color: blue}
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
I'm trying to blend two ImageData objects into a single object in order to obtain result similar to the pictures shown in this link
The following is the Javascript code that has the two ImageData
var redImage = copy.getImageData((SCREEN_WIDTH - VIDEO_WIDTH)/2,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2,VIDEO_WIDTH,VIDEO_HEIGHT);
var bluImage = copy.getImageData((SCREEN_WIDTH - VIDEO_WIDTH)/2,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2,VIDEO_WIDTH,VIDEO_HEIGHT);
var redData = redImage.data;
var blueData = blueImage.data;
// Colorize red
for(var i = 0; i < redData.length; i+=4) {
redData[i] -= (redData[i] - 255);
}
redImage.data = redData;
// Draw the pixels onto the visible canvas
disp.putImageData(redImage,(SCREEN_WIDTH - VIDEO_WIDTH)/2 - 25,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2);
// Colorize cyan
for(var i = 1; i < blueData.length; i+=4) {
blueData[i] -= (blueData[i] - 255);
blueData[i+1] -= (blueData[i+1] - 255);
}
blueImage.data = blueData;
// Draw the pixels onto the visible canvas
disp.putImageData(blueImage,(SCREEN_WIDTH - VIDEO_WIDTH)/2 + 25,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2);
How do i merge/blend the redData and blueData before putting it on the canvas ?
The formula you can use to mix two images is fairly simple:
newPixel = imageMainPixel * mixFactor + imageSecPixel * (1 - mixFactor)
Example assuming both buffers are of equal length:
var mixFactor = 0.5; //main image is dominant
//we're using the red buffer as main buffer for this example
for(var i = 0; i < redData.length; i+=4) {
redData[i] = redData[i] * mixFactor + blueData[i] * (1 - mixFactor);
redData[i+1] = redData[i+1] * mixFactor + blueData[i+1] * (1 - mixFactor);
redData[i+2] = redData[i+2] * mixFactor + blueData[i+2] * (1 - mixFactor);
}
Now your red buffer contains the mixed image.
To add an offset you can simply redraw the images with an offset value, for example:
var offset = 20; //pixels
copy.drawImage(originalImage, -offset, 0); // <--
var redImage = copy.getImageData( /*...*/ );
copy.drawImage(originalImage, offset, 0); // -->
var bluImage = copy.getImageData( /*...*/ );
If you have not onlyImageDataobjects, but also sourcecanvaselements, you can use this method.
You can obtain base64-encoded image data by callingtoDataURLcanvas method. Then you can createImageelement from that data and then paste that image to destination canvas viadrawImage.
Example code:
function mergeImageData(callback, sources) {
var canvas = document.createElement('canvas'),
context,
images = Array.prototype.slice.call(arguments, 1).map(function(canvas) {
var img = new Image();
img.onload = onLoad;
img.src = canvas.toDataURL();
return img;
}
),
imgCounter = 0,
widths = [],
heights = [];
function onLoad() {
widths.push(this.width);
heights.push(this.height);
if (++imgCounter == images.length) {
merge();
};
};
function merge() {
canvas.width = Math.max.apply(null, widths);
canvas.height = Math.max.apply(null, heights);
context = canvas.getContext('2d');
images.forEach(function(img) {
context.drawImage(img, 0, 0, img.width, img.height);
}
);
callback(context.getImageData(0, 0, canvas.width, canvas.height));
};
};
what about functions of setting the transmission format 3d - from format full side by side to anaglyph, alternating rows, alternating columns, chessboard, original side by side and 2d from 3d ?
I'm building a new website that will let users apply filters to images (just like Instagram). I will use -webkit-filter for that.
The user must be able to save the filtered image. There is any way I can do that using JavaScript?
You can't save images directly, but you can render them in Canvas, then save from there.
See: Save HTML5 canvas with images as an image
There is no direct/straight forward method to export an image with CSS Filter.
Follow the below steps for Saving/Exporting an Image with -webkit-filter applied on it:
1. Render the image to a canvas:
var canvas = document.createElement('canvas');
canvas.id="canvasPhoto";
canvas.width = imageContaainer.width;
canvas.height = imageContaainer.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(imageContaainer, 0, 0, canvas.width, canvas.height);
Get the ImageData from canvas and apply the filter. Eg: I will apply grayscale filter to the ImageData below:
function grayscale(ctx) {
var pixels = ctx.getImageData(0,0,canvas.width, canvas.height);
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
var r = d[i];
var g = d[i+1];
var b = d[i+2];
var v = 0.2126*r + 0.7152*g + 0.0722*b;
d[i] = d[i+1] = d[i+2] = v
}
context.putImageData(pixels, 0, 0);
}
Add an event and use the below code to trigger download
function download(canvas) {
var data = canvas.toDataURL("image/png");
if (!window.open(data))
{
document.location.href = data;
}
}