I'm trying to manipulate the pixels of an image to invert the color and write back to the canvas. But it's not working. Here's the code:
<script type="text/javascript">
<!--
window.addEventListener('load', function () {
var elem = document.getElementById('myCanvas');
var context = elem.getContext('2d');
var img = new Image();
img.addEventListener('load', function () {
var x = 0, y = 0;
context.drawImage(this, x, y);
var imgd = context.getImageData(x, y, this.width, this.height);
var pix = imgd.data;
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = 255 - pix[i ]; // red
pix[i+1] = 255 - pix[i+1]; // green
pix[i+2] = 255 - pix[i+2]; // blue
}
context.putImageData(imgd, x, y);
}, false);
img.src = 'test.jpg';
}, false);
// -->
</script>
And the 'Test.jpg' is on the same folder as the script. Am I missing anything? It displays the same image without inverting.
the answer to your question is right here
Tested on chrome, and it seem to be the file:// scheme that breaks it. When I moved the script to my local server (http://) instead of running the file (file://), it worked!
Proof:
I just tried that example and it seemed to work for me fine. I got a negative/inverted image rendered. Using Firefox 10.0.2
Related
I have an ASP.NET application that allows users to click or tap on a Canvas to indicate pain locations on a body image. A body image is displayed on the Canvas and is the same size as the Canvas.
function drawBodyMap() {
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
var imageObj = new Image();
imageObj.src = 'https://.../body.jpg';
imageObj.onload = function () {
ctx.drawImage(imageObj, 0, 0, 600, 367);
};
}
<canvas id="myCanvas" width="600" height="367"></canvas>
<script>
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
canvas.addEventListener('mouseup', function (evt) {
if (ixPos > 9)
return;
var mousePos = getMousePos(canvas, evt);
bodyX[ixPos] = mousePos.x;
bodyY[ixPos] = mousePos.y;
painType[ixPos] = pain_type;
ixPos++;
ctx.beginPath();
ctx.arc(mousePos.x, mousePos.y, 8, 0, 2 * Math.PI);
if (pain_type == 1)
ctx.fillStyle = "#DC143C";
else if (pain_type == 2)
ctx.fillStyle = "#EA728A";
else if (pain_type == 3)
ctx.fillStyle = "#DAA520";
else if (pain_type == 4)
ctx.fillStyle = "#008000";
else if (pain_type == 5)
ctx.fillStyle = "#4169E1";
ctx.fill();
}, false);
</script>
The X,Y points added to the Canvas on the body image are saved to a database. These points are then loaded into a WPF application that displays the same body image on an XAML Canvas. C# code then adds the points over the image.
WPF CODE:
private void DisplayBodyPain()
{
List<BodyPain> pain = gFunc.sws.GetBodyPain(MemberID);
foreach (BodyPain bp in pain)
{
Border b = new Border();
b.Tag = bp.PainType.ToString();
b.Cursor = Cursors.Hand;
b.Width = 16;
b.Height = 16;
b.CornerRadius = new CornerRadius(8);
b.Background = GetPainBrush((byte)bp.PainType);
cvsBody.Children.Add(b);
Canvas.SetTop(b, bp.YPos);
Canvas.SetLeft(b, bp.XPos);
}
}
The problem I have is that the points drawn on the XAML Canvas are all slightly different from the points that were drawn on the HTML Canvas. Each point is not in exactly the same location.
Is there a way I can fix this? Should I be doing it differently?
HTML Canvas
WPF Canvas
I think you need to subtract the size of the marker from the coordinate where you want to place it. For the last two lines, try this instead:
Canvas.SetTop(b, bp.YPos - (b.Height / 2));
Canvas.SetLeft(b, bp.XPos - (b.Width / 2));
By subtracting half the marker's height and width, the center of the marker is placed on the desired coordinates.
With the plugin i found earlier on stackoverflow. Drawing has become smooth and nice. What i want is to only get the image part which i draw cropped from the canvas as an output and not the complete canvas. Can somebody help.
This is the code i am using for my canvas now: http://jsfiddle.net/sVsZL/1/
function canvasDisplay() {
var c=document.getElementById("canvas");
canvasImage=c.toDataURL("image/png");
document.getElementById("SSMySelectedImage").src=canvasImage;
}
Adding another answer because the other one was completely off.
Live Demo
What you need essentially is to keep track of a bounding box. What I do is create an object that holds the min values and max values of where you've drawn. This enables you to keep track of how big the image is and where it begins/ends.
this.dim = {minX : 9999, minY : 9999, maxX : 0, maxY : 0};
Then I created a function that checks the bounds.
this.setDimensions = function(x,y){
if(x < this.dim.minX){
this.dim.minX = x;
}
if(y < this.dim.minY){
this.dim.minY = y;
}
if(x > this.dim.maxX){
this.dim.maxX= x;
}
if(y > this.dim.maxY){
this.dim.maxY = y;
}
}
Make sure to check during clicking or moving.
this.mousedown = function(ev) {
tool.setDimensions(ev._x,ev._y);
};
this.mousemove = function(ev) {
tool.setDimensions(ev._x,ev._y);
};
And this is just a sample function that draws the portion to a new canvas that you could then save with toDataUrl
var button = document.getElementsByTagName("input")[0];
button.addEventListener("click", function(){
var savedCanvas = document.createElement("canvas"),
savedCtx = savedCanvas.getContext("2d"),
minX = PEN.dim.minX,
minY = PEN.dim.minY,
maxX = PEN.dim.maxX,
maxY = PEN.dim.maxY,
width = maxX - minX,
height = maxY - minY;
savedCanvas.width = width;
savedCanvas.height = height;
document.body.appendChild(savedCanvas);
savedCtx.drawImage(canvas,minX,minY,width,height,0,0,width,height);
});
I have some JS that makes some manipulations with images. I want to have pixelart-like graphics, so I had to enlarge original images in graphics editor.
But I think it'd be good idea to make all the manipulations with the small image and then enlarge it with html5 functionality. This will save bunch of processing time (because now my demo (warning: domain-name may cause some issues at work etc) loads extremely long in Firefox, for example).
But when I try to resize the image, it gets resampled bicubically. How to make it resize image without resampling? Is there any crossbrowser solution?
image-rendering: -webkit-optimize-contrast; /* webkit */
image-rendering: -moz-crisp-edges /* Firefox */
http://phrogz.net/tmp/canvas_image_zoom.html can provide a fallback case using canvas and getImageData. In short:
// Create an offscreen canvas, draw an image to it, and fetch the pixels
var offtx = document.createElement('canvas').getContext('2d');
offtx.drawImage(img1,0,0);
var imgData = offtx.getImageData(0,0,img1.width,img1.height).data;
// Draw the zoomed-up pixels to a different canvas context
for (var x=0;x<img1.width;++x){
for (var y=0;y<img1.height;++y){
// Find the starting index in the one-dimensional image data
var i = (y*img1.width + x)*4;
var r = imgData[i ];
var g = imgData[i+1];
var b = imgData[i+2];
var a = imgData[i+3];
ctx2.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
ctx2.fillRect(x*zoom,y*zoom,zoom,zoom);
}
}
More: MDN docs on image-rendering
I wrote a NN resizing script a while ago using ImageData (around line 1794)
https://github.com/arahaya/ImageFilters.js/blob/master/imagefilters.js
You can see a demo here
http://www.arahaya.com/imagefilters/
unfortunately the builtin resizing should be slightly faster.
This CSS on the canvas element works:
image-rendering: pixelated;
This works in Chrome 93, as of September 2021.
You can simply set context.imageSmoothingEnabled to false. This will make everything drawn with context.drawImage() resize using nearest neighbor.
// the canvas to resize
const canvas = document.createElement("canvas");
// the canvas to output to
const canvas2 = document.createElement("canvas");
const context2 = canvas2.getContext("2d");
// disable image smoothing
context2.imageSmoothingEnabled = false;
// draw image from the canvas
context2.drawImage(canvas, 0, 0, canvas2.width, canvas2.height);
This has better support than using image-rendering: pixelated.
I'll echo what others have said and tell you it's not a built-in function. After running into the same issue, I've made one below.
It uses fillRect() instead of looping through each pixel and painting it. Everything is commented to help you better understand how it works.
//img is the original image, scale is a multiplier. It returns the resized image.
function Resize_Nearest_Neighbour( img, scale ){
//make shortcuts for image width and height
var w = img.width;
var h = img.height;
//---------------------------------------------------------------
//draw the original image to a new canvas
//---------------------------------------------------------------
//set up the canvas
var c = document.createElement("CANVAS");
var ctx = c.getContext("2d");
//disable antialiasing on the canvas
ctx.imageSmoothingEnabled = false;
//size the canvas to match the input image
c.width = w;
c.height = h;
//draw the input image
ctx.drawImage( img, 0, 0 );
//get the input image as image data
var inputImg = ctx.getImageData(0,0,w,h);
//get the data array from the canvas image data
var data = inputImg.data;
//---------------------------------------------------------------
//resize the canvas to our bigger output image
//---------------------------------------------------------------
c.width = w * scale;
c.height = h * scale;
//---------------------------------------------------------------
//loop through all the data, painting each pixel larger
//---------------------------------------------------------------
for ( var i = 0; i < data.length; i+=4 ){
//find the colour of this particular pixel
var colour = "#";
//---------------------------------------------------------------
//convert the RGB numbers into a hex string. i.e. [255, 10, 100]
//into "FF0A64"
//---------------------------------------------------------------
function _Dex_To_Hex( number ){
var out = number.toString(16);
if ( out.length < 2 ){
out = "0" + out;
}
return out;
}
for ( var colourIndex = 0; colourIndex < 3; colourIndex++ ){
colour += _Dex_To_Hex( data[ i+colourIndex ] );
}
//set the fill colour
ctx.fillStyle = colour;
//---------------------------------------------------------------
//convert the index in the data array to x and y coordinates
//---------------------------------------------------------------
var index = i/4;
var x = index % w;
//~~ is a faster way to do 'Math.floor'
var y = ~~( index / w );
//---------------------------------------------------------------
//draw an enlarged rectangle on the enlarged canvas
//---------------------------------------------------------------
ctx.fillRect( x*scale, y*scale, scale, scale );
}
//get the output image from the canvas
var output = c.toDataURL("image/png");
//returns image data that can be plugged into an img tag's src
return output;
}
Below is an example of it in use.
Your image would appear in the HTML like this:
<img id="pixel-image" src="" data-src="pixel-image.png"/>
The data-src tag contains the URL for the image you want to enlarge. This is a custom data tag. The code below will take the image URL from the data tag and put it through the resizing function, returning a larger image (30x the original size) which then gets injected into the src attribute of the img tag.
Remember to put the function Resize_Nearest_Neighbour (above) into the <script> tag before you include the following.
function Load_Image( element ){
var source = element.getAttribute("data-src");
var img = new Image();
img.addEventListener("load", function(){
var bigImage = Resize_Nearest_Neighbour( this, 30 );
element.src = bigImage;
});
img.src = source;
}
Load_Image( document.getElementById("pixel-image") );
There is no built-in way. You have to do it yourself with getImageData.
Based on Paul Irish's comment:
function resizeBase64(base64, zoom) {
return new Promise(function(resolve, reject) {
var img = document.createElement("img");
// once image loaded, resize it
img.onload = function() {
// get image size
var imageWidth = img.width;
var imageHeight = img.height;
// create and draw image to our first offscreen canvas
var canvas1 = document.createElement("canvas");
canvas1.width = imageWidth;
canvas1.height = imageHeight;
var ctx1 = canvas1.getContext("2d");
ctx1.drawImage(this, 0, 0, imageWidth, imageHeight);
// get pixel data from first canvas
var imgData = ctx1.getImageData(0, 0, imageWidth, imageHeight).data;
// create second offscreen canvas at the zoomed size
var canvas2 = document.createElement("canvas");
canvas2.width = imageWidth * zoom;
canvas2.height = imageHeight * zoom;
var ctx2 = canvas2.getContext("2d");
// draw the zoomed-up pixels to a the second canvas
for (var x = 0; x < imageWidth; ++x) {
for (var y = 0; y < imageHeight; ++y) {
// find the starting index in the one-dimensional image data
var i = (y * imageWidth + x) * 4;
var r = imgData[i];
var g = imgData[i + 1];
var b = imgData[i + 2];
var a = imgData[i + 3];
ctx2.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")";
ctx2.fillRect(x * zoom, y * zoom, zoom, zoom);
}
}
// resolve promise with the zoomed base64 image data
var dataURI = canvas2.toDataURL();
resolve(dataURI);
};
img.onerror = function(error) {
reject(error);
};
// set the img soruce
img.src = base64;
});
}
resizeBase64(src, 4).then(function(zoomedSrc) {
console.log(zoomedSrc);
});
https://jsfiddle.net/djhyquon/69/
I got familiarize with canvas with the help of lot of resources available online, and trying to compare the same with svg. My application needs to draw limited number of shapes, but need to be interactive. I think svg would be more suitable being the shapes are dom elements. it would be great help if someone can translate the canvas example (see demo) to svg with only dependency on jQuery and html5 (don't worry about IE)
In the example, I need to draw a rectangle using mouse (left click and drag). you may add each element to the dom (in canvas I may have to keep an array for the rect object, as the screen clears on each event).
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="draw.js"></script>
</head>
<body>
<canvas id="cvs" height="600" width="800"></canvas>
</body>
< /html>
$(document).ready(function() {
var cvs = $("#cvs"),
ctx = cvs.get(0).getContext("2d");
var v_bufX, v_bufY, v_bufW, v_bufH;
var box = function ( ctx, style, x, y, w, h ) {
ctx.beginPath();
ctx.rect( x, y, w, h );
ctx.closePath();
if ( style.fill ) {
ctx.fillStyle = style.fill;
ctx.fill();
}
if ( style.stroke ) {
ctx.strokeStyle = style.stroke;
ctx.lineWidth = style.width || 1;
ctx.stroke();
}
},
draw = function (res) {
var style = {fill:'rgba(96,185,206, 0.3)',stroke:'rgb(96,185,206)',width:.5};
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
box(ctx, style, res.x, res.y, res.w, res.h);
};
var rect = {
reset : function () {
this.x0 = this.y0 = this.x = this.y = this.w = this.h = -1;
this.started = this.dragging = false;
},
mousedown : function (e) {
this.reset();
this.started = true;
this.x0 = e._x;
this.y0 = e._y;
},
mousemove : function (e) {
if (!this.started) {
return;
}
var x = Math.min(e._x, this.x0),
y = Math.min(e._y, this.y0),
w = Math.abs(e._x - this.x0),
h = Math.abs(e._y - this.y0);
console.log(x, y, w, h);
if (!w || !h) {
return;
};
this.x = x;
this.y = y;
this.w = w;
this.h = h;
draw(this);
},
mouseup : function (ev) {
if (this.started) {
this.mousemove(ev);
this.started = false;
draw(this);
}
}
};
$(window).mousedown(function(e) {
var canvasOffset = cvs.offset();
e._x = Math.floor(e.pageX-canvasOffset.left);
e._y = Math.floor(e.pageY-canvasOffset.top);
rect.mousedown(e);
});
$(window).mousemove(function(e) {
var canvasOffset = cvs.offset();
e._x = Math.floor(e.pageX-canvasOffset.left);
e._y = Math.floor(e.pageY-canvasOffset.top);
rect.mousemove(e);
});
$(window).mouseup(function(e) {
var canvasOffset = cvs.offset();
e._x = Math.floor(e.pageX-canvasOffset.left);
e._y = Math.floor(e.pageY-canvasOffset.top);
rect.mouseup(e);
});
});
I'm not willing to rewrite an entire example, but here are some resources that might help:
Embedding SVG in XHTML5 - includes a simple JavaScript that creates some of the elements programmtically.
Dragging Transformed Elements - uses my own dragging code and accounts for translations in transformed hierarchies.
SVGPan - a nice library for panning and zooming
Raphael - a library designed to create SVG/VML (for old IE) from JavaScript, including its own draggable implementation.
KevLinDev - a venerable but incredibly-rich source of tutorials and code related to SVG.
I am trying to move an image from the right to the center and I am not sure if this is the best way.
var imgTag = null;
var x = 0;
var y = 0;
var id;
function doCanvas()
{
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
var imgBkg = document.getElementById('imgBkg');
imgTag = document.getElementById('imgTag');
ctx.drawImage(imgBkg, 0, 0);
x = canvas.width;
y = 40;
id = setInterval(moveImg, 0.25);
}
function moveImg()
{
if(x <= 250)
clearInterval(id);
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
var imgBkg = document.getElementById('imgBkg');
ctx.drawImage(imgBkg, 0, 0);
ctx.drawImage(imgTag, x, y);
x = x - 1;
}
Any advice?
This question is 5 years old, but since we now have requestAnimationFrame() method, here's an approach for that using vanilla JavaScript:
var imgTag = new Image(),
canvas = document.getElementById('icanvas'),
ctx = canvas.getContext("2d"),
x = canvas.width,
y = 0;
imgTag.onload = animate;
imgTag.src = "http://i.stack.imgur.com/Rk0DW.png"; // load image
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
ctx.drawImage(imgTag, x, y); // draw image at current position
x -= 4;
if (x > 250) requestAnimationFrame(animate) // loop
}
<canvas id="icanvas" width=640 height=180></canvas>
drawImage() enables to define which part of the source image to draw on target canvas. I would suggest for each moveImg() calculate the previous image position, overwrite the previous image with that part of imgBkg, then draw the new image. Supposedly this will save some computing power.
Here's my answer.
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var myImg = new Image();
var myImgPos = {
x: 250,
y: 125,
width: 50,
height: 25
}
function draw() {
myImg.onload = function() {
ctx.drawImage(myImg, myImgPos.x, myImgPos.y, myImgPos.width, myImgPos.height);
}
myImg.src = "https://mario.wiki.gallery/images/thumb/c/cc/NSMBUD_Mariojump.png/1200px-NSMBUD_Mariojump.png";
}
function moveMyImg() {
ctx.clearRect(myImgPos.x, myImgPos.y, myImgPos.x + myImgPos.width, myImgPos.y +
myImgPos.height);
myImgPos.x -= 5;
}
setInterval(draw, 50);
setInterval(moveMyImg, 50);
<canvas id="canvas" class="canvas" width="250" height="150"></canvas>
For lag free animations,i generally use kinetic.js.
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 6,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(hexagon);
stage.add(layer);
var amplitude = 150;
var period = 2000;
// in ms
var centerX = stage.width()/2;
var anim = new Kinetic.Animation(function(frame) {
hexagon.setX(amplitude * Math.sin(frame.time * 2 * Math.PI / period) + centerX);
}, layer);
anim.start();
Here's the example,if you wanna take a look.
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-animate-position-tutorial/
Why i suggest this is because,setInterval or setTimeout a particular function causes issues when large amount of simultaneous animations take place,but kinetic.Animation deals with framerates more intelligently.
Explaining window.requestAnimationFrame() with an example
In the following snippet I'm using an image for the piece that is going to be animated.
I'll be honest... window.requestAnimationFrame() wasn't easy for me to understand, that is why I coded it as clear and intuitive as possible. So that you may struggle less than I did to get my head around it.
const
canvas = document.getElementById('root'),
btn = document.getElementById('btn'),
ctx = canvas.getContext('2d'),
brickImage = new Image(),
piece = {image: brickImage, x:400, y:70, width:70};
brickImage.src = "https://i.stack.imgur.com/YreH6.png";
// When btn is clicked execute start()
btn.addEventListener('click', start)
function start(){
btn.value = 'animation started'
// Start gameLoop()
brickImage.onload = window.requestAnimationFrame(gameLoop)
}
function gameLoop(){
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Draw at coordinates x and y
ctx.drawImage(piece.image, piece.x, piece.y)
let pieceLeftSidePos = piece.x;
let middlePos = canvas.width/2 - piece.width/2;
// Brick stops when it gets to the middle of the canvas
if(pieceLeftSidePos > middlePos) piece.x -= 2;
window.requestAnimationFrame(gameLoop) // Needed to keep looping
}
<input id="btn" type="button" value="start" />
<p>
<canvas id="root" width="400" style="border:1px solid grey">
A key point
Inside the start() function we have:
brickImage.onload = window.requestAnimationFrame(gameLoop);
This could also be written like: window.requestAnimationFrame(gameLoop);
and it would probably work, but I'm adding the brickImage.onload to make sure that the image has loaded first. If not it could cause some issues.
Note: window.requestAnimationFrame() usually loops at 60 times per second.