Resizing an Image in an Html5 Canvas

Resize image with javascript canvas (smoothly)

You can use down-stepping to achieve better results. Most browsers seem to use linear interpolation rather than bi-cubic when resizing images.

(Update There has been added a quality property to the specs, imageSmoothingQuality which is currently available in Chrome only.)

Unless one chooses no smoothing or nearest neighbor the browser will always interpolate the image after down-scaling it as this function as a low-pass filter to avoid aliasing.

Bi-linear uses 2x2 pixels to do the interpolation while bi-cubic uses 4x4 so by doing it in steps you can get close to bi-cubic result while using bi-linear interpolation as seen in the resulting images.

var canvas = document.getElementById("canvas");var ctx = canvas.getContext("2d");var img = new Image();
img.onload = function () {
// set size proportional to image canvas.height = canvas.width * (img.height / img.width);
// step 1 - resize to 50% var oc = document.createElement('canvas'), octx = oc.getContext('2d');
oc.width = img.width * 0.5; oc.height = img.height * 0.5; octx.drawImage(img, 0, 0, oc.width, oc.height);
// step 2 octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
// step 3, resize to final size ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5, 0, 0, canvas.width, canvas.height);}img.src = "//i.imgur.com/SHo6Fub.jpg";
<img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234"><canvas id="canvas" width=300></canvas>

Resizing an image in an HTML5 canvas

So what do you do if all the browsers (actually, Chrome 5 gave me quite good one) won't give you good enough resampling quality? You implement them yourself then! Oh come on, we're entering the new age of Web 3.0, HTML5 compliant browsers, super optimized JIT javascript compilers, multi-core(†) machines, with tons of memory, what are you afraid of? Hey, there's the word java in javascript, so that should guarantee the performance, right? Behold, the thumbnail generating code:

// returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
return function(x) {
if (x > lobes)
return 0;
x *= Math.PI;
if (Math.abs(x) < 1e-16)
return 1;
var xx = x / lobes;
return Math.sin(x) * Math.sin(xx) / x / xx;
};
}

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
this.canvas = elem;
elem.width = img.width;
elem.height = img.height;
elem.style.display = "none";
this.ctx = elem.getContext("2d");
this.ctx.drawImage(img, 0, 0);
this.img = img;
this.src = this.ctx.getImageData(0, 0, img.width, img.height);
this.dest = {
width : sx,
height : Math.round(img.height * sx / img.width),
};
this.dest.data = new Array(this.dest.width * this.dest.height * 3);
this.lanczos = lanczosCreate(lobes);
this.ratio = img.width / sx;
this.rcp_ratio = 2 / this.ratio;
this.range2 = Math.ceil(this.ratio * lobes / 2);
this.cacheLanc = {};
this.center = {};
this.icenter = {};
setTimeout(this.process1, 0, this, 0);
}

thumbnailer.prototype.process1 = function(self, u) {
self.center.x = (u + 0.5) * self.ratio;
self.icenter.x = Math.floor(self.center.x);
for (var v = 0; v < self.dest.height; v++) {
self.center.y = (v + 0.5) * self.ratio;
self.icenter.y = Math.floor(self.center.y);
var a, r, g, b;
a = r = g = b = 0;
for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
if (i < 0 || i >= self.src.width)
continue;
var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
if (!self.cacheLanc[f_x])
self.cacheLanc[f_x] = {};
for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
if (j < 0 || j >= self.src.height)
continue;
var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
if (self.cacheLanc[f_x][f_y] == undefined)
self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
+ Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
weight = self.cacheLanc[f_x][f_y];
if (weight > 0) {
var idx = (j * self.src.width + i) * 4;
a += weight;
r += weight * self.src.data[idx];
g += weight * self.src.data[idx + 1];
b += weight * self.src.data[idx + 2];
}
}
}
var idx = (v * self.dest.width + u) * 3;
self.dest.data[idx] = r / a;
self.dest.data[idx + 1] = g / a;
self.dest.data[idx + 2] = b / a;
}

if (++u < self.dest.width)
setTimeout(self.process1, 0, self, u);
else
setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
self.canvas.width = self.dest.width;
self.canvas.height = self.dest.height;
self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
var idx, idx2;
for (var i = 0; i < self.dest.width; i++) {
for (var j = 0; j < self.dest.height; j++) {
idx = (j * self.dest.width + i) * 3;
idx2 = (j * self.dest.width + i) * 4;
self.src.data[idx2] = self.dest.data[idx];
self.src.data[idx2 + 1] = self.dest.data[idx + 1];
self.src.data[idx2 + 2] = self.dest.data[idx + 2];
}
}
self.ctx.putImageData(self.src, 0, 0);
self.canvas.style.display = "block";
};

...with which you can produce results like these!

img717.imageshack.us/img717/8910/lanczos358.png

so anyway, here is a 'fixed' version of your example:

img.onload = function() {
var canvas = document.createElement("canvas");
new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
// but feel free to raise it up to 8. Your client will appreciate
// that the program makes full use of his machine.
document.body.appendChild(canvas);
};

Now it's time to pit your best browsers out there and see which one will least likely increase your client's blood pressure!

Umm, where's my sarcasm tag?

(since many parts of the code is based on Anrieff Gallery Generator is it also covered under GPL2? I don't know)

actually due to limitation of javascript, multi-core is not supported.

Resize image in html5 canvas

Here is a snippet to help you for that

var ctx = document.getElementById('canvas').getContext('2d'); var img = new Image(); var fill = true; if (fill) {  $('#fill').attr("disabled", true); }$("#Photo").change(function (e) {    img.src = URL.createObjectURL(e.target.files[0]);    img.onload = function () {    if (fill)      {    drowImageFill(img);      }      else      {         drowImageCenter(img);      }    }});
$("#fill").click(function(){ //console.log("fill"); var input = document.getElementById('Photo'); if (input.files[0] !== undefined) { img.src = URL.createObjectURL(input.files[0]); img.onload = function () { drowImageFill(img); } } $('#fill').attr("disabled", true); $('#center').attr("disabled", false); fill = true;});$("#center").click(function(){ //console.log("center"); var input = document.getElementById('Photo'); if (input.files[0] !== undefined) { img.src = URL.createObjectURL(input.files[0]); img.onload = function () { drowImageCenter(img); } } $('#center').attr("disabled", true); $('#fill').attr("disabled", false); fill = false;});//ratio formula//http://andrew.hedges.name/experiments/aspect_ratio/function drowImageFill(img){ ctx.clearRect(0, 0, canvas.width, canvas.height); //detect closet value to canvas edges if( img.height / img.width * canvas.width > canvas.height) { // fill based on less image section loss if width matched var width = canvas.width; var height = img.height / img.width * width; offset = (height - canvas.height) / 2; ctx.drawImage(img, 0, -offset, width, height); } else { // fill based on less image section loss if height matched var height = canvas.height; var width = img.width / img.height * height; offset = (width - canvas.width) / 2; ctx.drawImage(img, -offset , 0, width, height); } }
function drowImageCenter(img){ ctx.clearRect(0, 0, canvas.width, canvas.height); if( img.height / img.width * canvas.width < canvas.height) { // center based on width var width = canvas.width; var height = img.height / img.width * width; offset = (canvas.height - height) / 2; ctx.drawImage(img, 0, offset, width, height); } else { // center based on height var height = canvas.height; var width = img.width / img.height * height; offset = (canvas.width - width) / 2; ctx.drawImage(img, offset , 0, width, height); }}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><canvas id="canvas" width="300" height="200" style="border:2px solid #000000;"></canvas><form class="cmxform">      <input type='file' id="Photo" /></form>
<button id="fill">Fill</button><button id="center">Center</button>

Resizing image with HTML5 canvas

Setting an image source is asynchronous, can be very fast, but often not fast enough to keep up with still-running code. Generally, to make them work reliably, you set an onload handler first and then set src. The canvas element defaults to 300x150 so would also need to be sized. (Canvas obeys CORS. .crossOrigin = '' sets us as anonymous and imgur has a permissive CORS policy. We wouldn't be able to convert the canvas to an image while using a third-party image in this snippet otherwise.)

const MAX_WIDTH = 400;
const MAX_HEIGHT = 300;
const img = new Image();
img.crossOrigin = '';
img.onload = () => {
const wRatio = MAX_WIDTH / img.width;
const hRatio = MAX_HEIGHT / img.height;
var width, height;
if(wRatio > hRatio) {
width = MAX_WIDTH;
height = wRatio * img.height;
}
else {
width = hRatio * img.width;
height = MAX_HEIGHT;
}
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
//const finalImage = b64toFile(canvas.toDataURL("image/jpg"));
const imgElement = document.createElement('img');
imgElement.src = canvas.toDataURL('image/jpg');
document.body.appendChild(imgElement);
};
img.src = 'https://i.imgur.com/TMeawxt.jpeg';
img { border: 1px solid red; }

dragging and resizing an image on html5 canvas

Here's example code to allow you to drag and resize an image using Canvas.

Resizing

Sample ImageSample Image

How to resize an image with 4 draggable anchors

  • Draw a draggable anchor on each corner of an image.
  • If the user mousedown’s one if the anchors, start dragging that anchor.
  • In the mousemove handler, resize the image using the dragging anchor’s position(Note below).
  • As the last act in mousemove, redraw the resized image and 4 new anchors.
  • On mouseup, stop the anchor’s drag.

Note on the math used to resize the image:

  • The resized width is the difference between the mouseX position and the opposite corner’s X.
  • The resized height is the difference between the mouseY position and the opposite corner’s Y.

Dragging

Sample ImageSample Image

How to drag an image

  • If the user mousedown’s inside the image, save the mouses starting XY to begin dragging.
  • In the mousemove handler, move the image by the current mouseXY minus the startingXY.
  • Also in mousemove, reset the startingXY to the current mouseXY in preparation for continued dragging.
  • On mouseup, stop the image’s drag.

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/LAS8L/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
body{ background-color: ivory; padding:10px;}
#canvas{border:1px solid red;}
</style>

<script>
$(function(){

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;

var startX;
var startY;
var isDown=false;


var pi2=Math.PI*2;
var resizerRadius=8;
var rr=resizerRadius*resizerRadius;
var draggingResizer={x:0,y:0};
var imageX=50;
var imageY=50;
var imageWidth,imageHeight,imageRight,imageBottom;
var draggingImage=false;
var startX;
var startY;



var img=new Image();
img.onload=function(){
imageWidth=img.width;
imageHeight=img.height;
imageRight=imageX+imageWidth;
imageBottom=imageY+imageHeight
draw(true,false);
}
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/facesSmall.png";


function draw(withAnchors,withBorders){

// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);

// draw the image
ctx.drawImage(img,0,0,img.width,img.height,imageX,imageY,imageWidth,imageHeight);

// optionally draw the draggable anchors
if(withAnchors){
drawDragAnchor(imageX,imageY);
drawDragAnchor(imageRight,imageY);
drawDragAnchor(imageRight,imageBottom);
drawDragAnchor(imageX,imageBottom);
}

// optionally draw the connecting anchor lines
if(withBorders){
ctx.beginPath();
ctx.moveTo(imageX,imageY);
ctx.lineTo(imageRight,imageY);
ctx.lineTo(imageRight,imageBottom);
ctx.lineTo(imageX,imageBottom);
ctx.closePath();
ctx.stroke();
}

}

function drawDragAnchor(x,y){
ctx.beginPath();
ctx.arc(x,y,resizerRadius,0,pi2,false);
ctx.closePath();
ctx.fill();
}

function anchorHitTest(x,y){

var dx,dy;

// top-left
dx=x-imageX;
dy=y-imageY;
if(dx*dx+dy*dy<=rr){ return(0); }
// top-right
dx=x-imageRight;
dy=y-imageY;
if(dx*dx+dy*dy<=rr){ return(1); }
// bottom-right
dx=x-imageRight;
dy=y-imageBottom;
if(dx*dx+dy*dy<=rr){ return(2); }
// bottom-left
dx=x-imageX;
dy=y-imageBottom;
if(dx*dx+dy*dy<=rr){ return(3); }
return(-1);

}


function hitImage(x,y){
return(x>imageX && x<imageX+imageWidth && y>imageY && y<imageY+imageHeight);
}


function handleMouseDown(e){
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
draggingResizer=anchorHitTest(startX,startY);
draggingImage= draggingResizer<0 && hitImage(startX,startY);
}

function handleMouseUp(e){
draggingResizer=-1;
draggingImage=false;
draw(true,false);
}

function handleMouseOut(e){
handleMouseUp(e);
}

function handleMouseMove(e){

if(draggingResizer>-1){

mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);

// resize the image
switch(draggingResizer){
case 0: //top-left
imageX=mouseX;
imageWidth=imageRight-mouseX;
imageY=mouseY;
imageHeight=imageBottom-mouseY;
break;
case 1: //top-right
imageY=mouseY;
imageWidth=mouseX-imageX;
imageHeight=imageBottom-mouseY;
break;
case 2: //bottom-right
imageWidth=mouseX-imageX;
imageHeight=mouseY-imageY;
break;
case 3: //bottom-left
imageX=mouseX;
imageWidth=imageRight-mouseX;
imageHeight=mouseY-imageY;
break;
}

// enforce minimum dimensions of 25x25
if(imageWidth<25){imageWidth=25;}
if(imageHeight<25){imageHeight=25;}

// set the image right and bottom
imageRight=imageX+imageWidth;
imageBottom=imageY+imageHeight;

// redraw the image with resizing anchors
draw(true,true);

}else if(draggingImage){

imageClick=false;

mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);

// move the image by the amount of the latest drag
var dx=mouseX-startX;
var dy=mouseY-startY;
imageX+=dx;
imageY+=dy;
imageRight+=dx;
imageBottom+=dy;
// reset the startXY for next time
startX=mouseX;
startY=mouseY;

// redraw the image with border
draw(false,true);

}


}


$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});


}); // end $(function(){});
</script>

</head>

<body>
<p>Resize the image using the 4 draggable corner anchors</p>
<p>You can also drag the image</p>
<canvas id="canvas" width=350 height=350></canvas>
</body>
</html>

HTML5 canvas: image resizing

Don't use CSS to size your canvas. That creates a default sized canvas and stretches it. Set the canvas dimensions directly on it and you'll get a 1 to 1 pixel drawing space.

<canvas id="story" width="800" height="600" style="position:relative;"></canvas>

How to resize images to fit my canvas?

You can use 4rth and 5th parameter of drawImage to define target width and height of the source image.

You can use:

void ctx.drawImage(image, dx, dy, dWidth, dHeight);

If you provide dx, dy as 0, 0 (as you are already providing in your example code) and dWidth and dHeight to canvas width and height, the source image will stretch to whole canvas.

If your source image has different aspect ratio and stretching it behind whole canvas disturbs it, you can use the following version:

void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth,
dHeight);

sx, sy are source image's start x, start y coordinate (which implicitly in previous version was 0, 0) and sWidth, sHeight are source image's width and height you want to pick and place on canvas.

So using start x, start y and width and height of source image, you can smartly choose a partition of source image so that when it stretches on whole canvas, aspect ratio should not get distorted.

MDN Reference.

Scaling an image to fit on canvas

Provide the source image (img) size as the first rectangle:

ctx.drawImage(img, 0, 0, img.width,    img.height,     // source rectangle
0, 0, canvas.width, canvas.height); // destination rectangle

The second rectangle will be the destination size (what source rectangle will be scaled to).

Update 2016/6: For aspect ratio and positioning (ala CSS' "cover" method), check out:

Simulation background-size: cover in canvas



Related Topics



Leave a reply



Submit