How to save a rotated canvas element to a file - javascript

I am trying to rotate a large image and save it as a file. I've tried several methods that fail due to the large image size. (OpenCV, Paint Shop Pro, etc).
So, I am trying to do this with javascript. I can draw the image onto a rotated canvas (this works), but when I try to save the canvas to a file, the rotation is lost. It makes sense that this happens, but would like to understand how to capture/extract the rotated state of the canvas before saving?
The client code:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
function initPage() {
var imageObj = new Image();
imageObj.onload = function() {
// Draw onto rotated canvas
var canvas = document.getElementById('mapCanvas');
context = canvas.getContext('2d');
context.drawImage(imageObj, 0, 0);
// Save image as png
var dt = canvas.toDataURL('image/png');
$.ajax({
type: "POST",
url: "savePNG.php?file=FN617_ROT",
data: {
imgBase64: dt
}
}).done(function(o) {
console.log('saved');
});
};
imageObj.src = 'FN617.png';
}
</script>
<style>
body {
margin: 0;
padding: 0;
}
#mapCanvas {
transform-origin:center center;
transform: rotate(-32.5deg);
}
</style>
</head>
<body onload="initPage();">
<canvas id="mapCanvas" width="9600" height="7200"></canvas>
</body>
</html>
The server code:
<?php
$base64 = str_replace('data:image/png;base64,', '', $_REQUEST['imgBase64']);
$data = base64_decode($base64);
file_put_contents(dirname(__FILE__)."/". $_GET['file'].".png", $data);
?>

When you capture canvas you are capturing the bitmap, not the element. CSS only affects element - as with a plain image, rotating it doesn't actually change the original image in any way.
In order to properly do this, first remove the CSS rotation.
Then apply rotation using the transformation methods of the context. Note here that CSS by default rotates around the center. This is not the case with canvas which rotates by the top left corner, so we need to translate first.
Here are the steps:
// translate to center where the rotation will take place:
context.translate(context.canvas.width * 0.5, context.canvas.height * 0.5);
// rotate (uses radians)
context.rotate = -32.5 * 180 / Math.PI;
// translate back as image is drawn from (0,0)
context.translate(-context.canvas.width * 0.5, -context.canvas.height * 0.5);
// draw image
context.drawImage(imageObj, 0, 0);
Optionally, if image does not correspond with the size of canvas, do the backward translation using the image's size instead.
context.translate(context.canvas.width * 0.5, context.canvas.height * 0.5);
context.rotate = -32.5 * 180 / Math.PI;
context.drawImage(imageObj, -imageObj.width * 0.5, -imageObj.height * 0.5);
Now you can capture canvas with a rotated image.
Note that a canvas of this size may not work in all browsers, and if it does it may not allow you to capture a data-uri of it as some browsers has size limits to those. Just something to have in mind..

You can do this with localStorage, but not with a file for security reasons.

Related

Canvas/Photo - getImageData not working

I am loading a picture into a canvas; I drawImage(), store getImageData() into a variable for manipulating. I would like to be able to manipulate the data many times for example: add/remove various filters. How can I store the data so that it updates every time I draw the picture with putImageData()?
Basically, I think I am misunderstanding the use of getImageData or using it incorrectly. My thought was that any manipulation that was done to the picture, I could run getImageData and update the variable that contained the information, and use it to "redraw" the picture.
Example:
In the snippet below Lets say I run a function that turn the picture black and white. I have another function that resizes the picture when it is clicked. When I resize the picture the the black and white filter disappears. What am I doing wrong to keep the information of the picture?
//Read in picture
var reader = new FileReader();
reader.onload = function leDraw(e){
imgObj = new Image();
picWidth = canvas.width/2;
picHeight = canvas.height/2;
imgObj.src = e.target.result;
newX = 0;
newY = 0;
ctx.drawImage(imgObj,0,0, picWidth, picHeight);
imageData = ctx.getImageData(newX,newY, canvas.width, canvas.height);
originalCopy = ctx.getImageData(newX,newY, picWidth, picHeight);
data = imageData.data;
function resize(val){ Resizes picture
userPicHeight = document.getElementById("cSelect").value;
userPicWidth = document.getElementById("cSelect").value;
ctx.clearRect(0,0, canvas.width, canvas.height);
ctx.drawImage(imgObj, newX, newY, userPicWidth, userPicHeight);
window['imageData'] = ctx.getImageData(newX,newY, userPicWidth, userPicHeight);
ctx.putImageData(imageData, newX, newY);
};
imageData is a snapshot of canvas pixel data. In your case it's the entire canvas's (colored--not B&W) pixel data.
So when you do .putImageData(imageData...) the unaltered snapshow is again displayed on the canvas.
If you want to rescale a B&W version of your picture:
Draw your color image on a new canvas created with var memCanvas = document.createElement. Size the canvas to the image size. The canvas can be left in-memory -- no need to appendChild it into the DOM.
Apply the filter to the new canvas with getImageData, modify pixel data, putImageData. Now you have an "image-canvas" that you can later use to resize, etc.
Draw the image-Canvas onto the visible canvas: context.drawImage(memCanvas,0,0). Yes, the memCanvas can be an image source for drawImage.
To scale the B&W version of the image, just clear the canvas, scale the canvas with context.scale & then draw the scaled B&W image with drawImage(memCanvas,0,0)
If you later want to re-rescale the B&W image, you can do Step#4 again.
Example code and a Demo using a grayscale filter:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/kingcard.png";
function start(){
// create a grayscale image-canvas
var grayImg=makeFilteredImageCanvas(img,grayscaleFilter);
// scale the visible canvas
ctx.scale(1.25,1.25);
// draw the grayscale imag-canvas on the canvas
// (the result will be scaled)
ctx.drawImage(grayImg,0,0);
}
function makeFilteredImageCanvas(img,filter){
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
iw=c.width=img.width;
ih=c.height=img.height;
cctx.drawImage(img,0,0);
filter(cctx);
return(c);
}
function grayscaleFilter(context){
var canvas=context.canvas;
var w=canvas.width;
var h=canvas.height;
var imageData=context.getImageData(0,0,w,h);
var data=imageData.data;
for(var i=0;i<data.length;i+=4){
var gray=data[i]*0.33+data[i+1]*0.5+data[i+2]*0.16;
data[i]=data[i+1]=data[i+2]=gray;
}
context.putImageData(imageData,0,0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<h4>Grayscale image scaled up by 25%</h4>
<canvas id="canvas" width=300 height=300></canvas>
<h4>Original Image:</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/kingcard.png' crossOrigin='anonymous'>

Extract individual frame from a sprite sheet using html or javascript

I have a sprite sheet consisting of 12 frames.
I want to extract each individual frame from it and want to show it in different canvas like below.
what i have tried so far is posted below
//HTML code for define the canvas area .
<body onload="image_render();">
<div id="image_area"> <canvas id="image"></canvas></div>
<script src="sprite_demo.js"></script>
</body>
// javascript to slice the image and assign to the canvas
var canvasImage = new Image();
canvasImage.src = "image/sprite_xxdpi.jpg";
var can= document.getElementById("image");
can.width = 500;
can.height = 300;
function image_render()
{
coin.render();
}
var coin = sprite({
context: can.getContext("2d"),
width: 500,
height: 300,
image: coinImage
});
function sprite (options) {
var that = {};
that.context = options.context;
that.width = options.width;
that.height = options.height;
that.image = options.image;
that.render = function () {
// Draw the animation
that.context.drawImage(
that.image,
0, //X-axis starting position from where slicing begins
0, //y-axis starting position from where slicing begins
that.width, //width of slicing image
that.height,//height of slicing image
0, //X-axis starting position where image will be drawn
0, //y-axis starting position where image will be drawn
150, // width of the resulting image
150); //height of the resulting image
};
return that;
}
I am only able to get a single image ,But I want to get all the images to show in a grid.And also i want to get the image to show any where I want.
I also want to scale down big size images to show in a grid and while taping on it I want to show the original image.
Note: I don't want to animate my frames, I just want to show in grid. There are mostly examples of sprite animation available on internet.
You have the correct version of drawImage to clip individual sprites from the spritesheet, but you must alter the values in drawImage for each sprite.
The "faces" example spritesheet you show appear to have equal sized individual sprites (75px by 75px).
Assuming all your sprites are the same size, you would alter the 2nd & 3rd drawImage parameters which tell canvas the top-left x/y coordinate to begin clipping on the spritesheet.
Here's example code and a Demo: http://jsfiddle.net/m1erickson/tVD2K/
<!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; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var spriteWidth=75;
var spriteHeight=75;
var spriteCols=4;
var spriteRows=3;
var y=20-sprightHeight;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/spritesheet1.jpg";
function start(){
var canvasY=0;
for(var col=0;col<spriteCols;col++){
for(var row=0;row<spriteRows;row++){
var sourceX=col*spriteWidth;
var sourceY=row*spriteHeight;
// testing: calc a random position to draw this sprite
// on the canvas
var canvasX=Math.random()*150+20;
canvasY+=spriteHeight+5;
// drawImage with changing source and canvas x/y positions
ctx.drawImage(img,
sourceX,sourceY,spriteWidth,spriteHeight,
canvasX,canvasY,spriteWidth,spriteHeight
);
}}
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Draw individual sprites from a spritesheet</h4>
<canvas id="canvas" width=300 height=1000></canvas>
</body>
</html>

How do I get pixi to expand my canvas and zoom in?

I'm making a game that uses pixi and it renders on a canvas that's 640x480 pixels. As you can imagine, this is very small when viewed on a PC. I'd like to accomplish this:
I want to increase the size of the canvas so it fills up the whole screen
I want to zoom in on the content so that it fills up as much as possible without changing its aspect ratio
I'd like to center the canvas if there's left over space from the previous step
When I google for how to do this in pixi, I can find each of these individually. But I'd like to have the information on how to do this all in one place and on stackoverflow, because you usually want to do all of these things together.
I modified the source code in this example made by the creator: http://www.goodboydigital.com/pixi-js-tutorial-getting-started/ (source download)
Here's what I came up with:
<!DOCTYPE HTML>
<html>
<head>
<title>pixi.js example 1</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #000000;
}
</style>
<script src="pixi.js"></script>
</head>
<body>
<script>
// create an new instance of a pixi stage
var stage = new PIXI.Stage(0x66FF99);
// create a renderer instance
var renderer = PIXI.autoDetectRenderer(400, 300);
renderer.resize(800, 600);
// add the renderer view element to the DOM
document.body.appendChild(renderer.view);
requestAnimFrame( animate );
// create a texture from an image path
var texture = PIXI.Texture.fromImage("bunny.png");
// create a new Sprite using the texture
var bunny = new PIXI.Sprite(texture);
// center the sprites anchor point
bunny.anchor.x = 0.5;
bunny.anchor.y = 0.5;
// move the sprite t the center of the screen
bunny.position.x = 200;
bunny.position.y = 150;
var container = new PIXI.DisplayObjectContainer();
container.scale.x = 2;
container.scale.y = 2;
container.addChild(bunny);
stage.addChild(container);
function animate() {
requestAnimFrame( animate );
// just for fun, lets rotate mr rabbit a little
bunny.rotation += 0.1;
// render the stage
renderer.render(stage);
}
</script>
</body>
</html>
Now the one thing I didn't do is center it. I see two potential ways to do this. I could use CSS to center the canvas (what I'll probably use), or I could do this in code by adding another outer display object to the stage that centers container.

Is there a way to use JCrop to crop an area larger than the actual image?

From what I can tell, JCrop will not let me set things up so the user can crop outside the actual image and include surrounding whitespace. Is there a way to do that?
To help explain what I mean, say we are restricting our crop to a 16:9 ratio. That works fine for an image with a naturally wide subject:
But sometimes the source image that a user wants to use does not comfortably accommodate the desired ratio:
Instead, we'd like to allow them to include space outside the image by making the crop area larger than the image itself:
I've been messing around with JCrop and looking through the manual and Google for a while and it doesn't look like this is possible (without modifying JCrop). Am I wrong? If so, how do you do it?
FWIW, the actual images in this case will be product/organization logo images, which come in a large variety of aspect ratios, and almost always the images available to people have almost no whitespace around the text/imagery. Which means any fixed aspect ratio crop restricted to the bounds of the image will almost certainly chop off either the top+bottom or left+right sides of the image.
My solution was to create a temporary canvas with square dimensions equal to the largest side of the image. I made the canvas background white and added the image in the center. Then I created a new image and used the canvas as the image source. Then I used that image with jcrop. It's slower, but it works!
Here's an example:
img.onload = function(){
// get the largest side of the image
// and set the x and y coordinates for where the image will go in the canvas
if( img.width > img.height ){
var largestDim = img.width;
var x = 0;
var y = (img.width-img.height)/2;
}
else{
var largestDim = img.height;
var y = 0;
var x = (img.height-img.width)/2;
}
// create a temporary canvas element and set its height and width to largestDim
canvastemp = document.createElement("canvas");
canvastemp.width = canvastemp.height = largestDim;
var ctx = canvastemp.getContext('2d');
// set the canvas background to white
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0, 0, canvastemp.width, canvastemp.height);
// center the image in the canvas
ctx.drawImage(img, x, y, img.width, img.height);
// create a new image and use the canvas as its source
var squaredImg = document.createElement("img");
squaredImg.src = canvastemp.toDataURL();
// add jcrop once the image loads
squaredImg.onload = function(){
addJcrop(squaredImg);
}
};
function addJcrop(img){
// your jcrop code
}
This way users can choose to include the entire image in the crop if they wish.
consider using something like php imagick to convert the photo to photo + transparent big background and then put that to JCrop I dont think its possible other way
You could fool the jCrop script. Instead of showing image.jpg, you do something like not_a_real_image.php?imagename=image.jpg.
Then give the php file a header of the image, and a width and height, and align the actual image in the center of that.
All you have to do is remember the amount of canvas you've added to correct it later on.
I made a function using Imagick:
function resizeImage($imgSrc, $width, $height, $createBg, $output, $show) {
$img = new Imagick($imgSrc);
if ($img->getImageWidth() / $img->getImageHeight() < $width / $height) {
$img->thumbnailImage(0, $height);
} else {
$img->thumbnailImage($width, 0);
}
$canvas = new Imagick();
$canvas->newImage($width, $height, 'white', 'jpg');
/* Creates a background image (good for vertical images in horizontal canvas or vice-versa) */
if ($createBg) {
$imgBg = new Imagick($imgSrc);
if ($imgBg->getImageWidth() / $imgBg->getImageHeight() < $width / $height) {
$imgBg->thumbnailImage($width, 0);
} else {
$imgBg->thumbnailImage(0, $height);
}
$imgBg->blurImage(0, 80);
$geometryBg = $imgBg->getImageGeometry();
$xBg = ( $width - $geometryBg['width'] ) / 2;
$yBg = ( $height - $geometryBg['height'] ) / 2;
$canvas->compositeImage( $imgBg, imagick::COMPOSITE_OVER, $xBg, $yBg );
}
/* Center image */
$geometry = $img->getImageGeometry();
$x = ( $width - $geometry['width'] ) / 2;
$y = ( $height - $geometry['height'] ) / 2;
$canvas->compositeImage( $img, imagick::COMPOSITE_OVER, $x, $y );
/* Save image */
if ($output) {
$canvas->writeImage($output);
}
/* Show the image */
if ($show) {
header( 'Content-Type: image/jpg' );
echo $canvas;
}
}
The comment's explain it all, enjoy!

HTML5 Canvas: How to make a loading spinner by rotating the image in degrees?

I am making a loading spinner with html5 canvas. I have my graphic on the canvas but when i rotate it the image rotates off the canvas. How do I tell it to spin the graphic on its center point?
<!DOCTYPE html>
<html>
<head>
<title>Canvas test</title>
<script type="text/javascript">
window.onload = function() {
var drawingCanvas = document.getElementById('myDrawing');
// Check the element is in the DOM and the browser supports canvas
if(drawingCanvas && drawingCanvas.getContext) {
// Initaliase a 2-dimensional drawing context
var context = drawingCanvas.getContext('2d');
//Load the image object in JS, then apply to canvas onload
var myImage = new Image();
myImage.onload = function() {
context.drawImage(myImage, 0, 0, 27, 27);
}
myImage.src = "img/loading.png";
context.rotate(45);
}
}
</script>
</head>
<body>
<canvas id="myDrawing" width="27" height="27">
</canvas>
</body>
</html>
Here is the complete working example:)
<!DOCTYPE html>
<html>
<head>
<title>Canvas Cog</title>
<script type="text/javascript">
var cog = new Image();
function init() {
cog.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABK1JREFUeNqMVt1ZGzkUvVfS4IW1l8GO82w6IBXE7mCpAFMB+Pt4Z6iApALcAe4AU0HoAJfg7BPYHinnXmmciX+y0YdmJHnQ0bk/R5cvh5cUyFPwRD4EChgEvGWMB36R3+JaiTkmD5gOs8yNb25uLlerFf1pM2yIGA82TEY7xow1oj4GBU6S6yywPNG4JwDH+XGv0Whs7ndN8n97mmPsLCSYgy7ImPQE/pFDyAF+7L0fgTNFUDBcLal90taD1doQ/T6NT9DnW8zkT+jJuQVYukG3hifCVk/L3JOxMBa8VVlSp9MhHKLaB+zpNo1fdgEpmByuMqUAV5viOQLwXNax9KBAFNEEpN1pUwnQmvl6aTza6zNjrCKaymeyOdYAMgfg18iG4T/qw+AC94zvpzDjcwqOXo3VGH26H0xMZ7jPxgT0R2zUi4BYt6bAfEbJvJFZKA4ODgZ5nhcJLE9mk35X21vWC/TXKmiwr2xszoQd/PQv3t/QCzY2twpqBpb5FKOp+hCgzWaTWq0W1Xx0ij5An9WC5VtiLMwvNBrVaSGMvQk5jHQVPN7sb0HzAtE+QJrNgrcUNEARieWCut0ugR0tl8sKcJ5Ahc3jRviPK8ZGTaaBwGKyT+gTiwM4a3Jrba6MbeVXo5F4kp9shn29ndUYC9vLirGDXzRhrYhD8DME5Hkg22df5rDYS/RXmVIsaP/Q/SXs600YnifTjbeSWliEdTYb3QyTqYfdDKTL4B1KS6tVqf6SgGq3P9BvZGpvNIrPCgVKZlGlCDQDxJiCjVppCab05DJHzb+b1Gm36X80cVjLuzozexs0f6IgRkA5XRhzIixRL1+IzhwdHVHrn1Y9oXe1i10aKT6bGGhg1CKK+cT0zCGCs0oXTIogybJMw/779//o48duMvnO9rzLn+Kz8wgS5Shqo4njpCoOQA5Ajb8adHh4SMvVghaLhYb/HsBip88krNVISSEigOlhjmi0LziNhr6wOsgO9C1339vbGznnNAU2AM9Svk235cqKieKGkldAf7DGvTrjnjJnzyQoMu0ZTuZgUqvmlYR+f39XIE4uqCX1E/rDZpCYmKwOOmivAfYK9KF1AM7EdG4uAMLAOjmQideQXOJQkyUisqYiFRhtSFbxCxj8do0T30dmTvLhC+an0MZZVBHX09tBTG4qFigZEJEChjTIEwtRik81Qa7uOQU0IrYAe7FRjqYw6SlYjgAyN1GmHsFIGPfVnxzFuFITKEkfYK+oWZ5qKlIkcZ7UE92oXBmeIgIxtAO5UtSHqo9uiLW+sme5ejSIRASeAFR4LYy8MMzL1aq3EYWzJF28BgMEzGYpBkrMKelgl+P6uTcVY8NjLYyYPwMTCcufSaouH6al9xNJcjC82vDb9uVZKbrWIumNO+waVsu1TCC+Wxcg6xaSpsZSYM2wLO9/U8qZWH+wztQnsfAxV/E3MIKZVf1FsmJVV8mamhEmxZ0X7sSsABsGv1tZJGejmptU7FBUDYzPAXQBwFEEl+9+stFEroJEci2ELwIMmZuWoSTE9DYYcWVCjlJrZWMpeBhlAEqBiulPE84S3ixU5gSTwGGOdyEVNJXxA8nPevshwABHktBS1YoQ+QAAAABJRU5ErkJggg=='; // Set source path
setInterval(draw,10);
}
var rotation = 0;
function draw(){
var ctx = document.getElementById('myCanvas').getContext('2d');
ctx.globalCompositeOperation = 'destination-over';
ctx.save();
ctx.clearRect(0,0,27,27);
ctx.translate(13.5,13.5); // to get it in the origin
rotation +=1;
ctx.rotate(rotation*Math.PI/64); //rotate in origin
ctx.translate(-13.5,-13.5); //put it back
ctx.drawImage(cog,0,0);
ctx.restore();
}
init();
</script>
</head>
<body>
<canvas width="27" height="27" id="myCanvas"></canvas>
</body>
</html>
rotate turns the canvas(?) around your current position, which is 0, 0 to start. you need to "move" to your desired center point, which you can accomplish with
context.translate(x,y);
after you move your reference point, you want to center your image over that point. you can do this by calling
context.drawImage(myImage, -(27/2), -(27/2), 27, 27);
this tells the browser to start drawing the image from above and to the left of your current reference point, by have the size of the image, whereas before you were starting at your reference point and drawing entirely below and to the right (all directions relative to the rotation of the canvas).
since your canvas is the size of your image, your call to translate will use the same measurement, (27/2), for x and y coordinates.
so, to put it all together
// initialization:
context.translate(27/2, 27/2);
// onload:
context.rotate(Math.PI * 45 / 180);
context.drawImage(myImage, -(27/2), -(27/2), 27, 27);
edit: also, rotation units are radians, so you'll need to translate degrees to radians in your code.
edits for rearranging stuff.
For anyone else looking into something like this, you might want to look at this script which does exactly what was originally being requested:
http://projects.nickstakenburg.com/spinners/
You can find the github source here:
https://github.com/staaky/spinners
He uses rotate, while keeping a cache of rectangles which slowly fade out, the older they are.
I find another way to do html loading spinner. You can use sprite sheet animation. This approach can work both by html5 canvas or normal html/javascript/css. Here is a simple way implemented by html/javascript/css.
It uses sprite sheet image as background. It create a Javascript timer to change the background image position to control the sprite sheet animation. The example code is below. You can also check the result here: http://jmsliu.com/1769/html-ajax-loading-spinner.html
<html>
<head><title></title></head>
<body>
<div class="spinner-bg">
<div id="spinner"></div>
</div>
<style>
.spinner-bg
{
width:44px;
height:41px;
background: #000000;
}
#spinner
{
width: 44px;
height: 41px;
background:url(./preloadericon.png) no-repeat;
}
</style>
<script>
var currentbgx = 0;
var circle = document.getElementById("spinner");
var circleTimer = setInterval(playAnimation, 100);
function playAnimation() {
if (circle != null) {
circle.style.backgroundPosition = currentbgx + "px 0";
}
currentbgx -= 44; //one frame width, there are 5 frame
//start from 0, end at 176, it depends on the png frame length
if (currentbgx < -176) {
currentbgx = 0;
}
}
</script>
</body>

Categories

Resources