Rendering html canvas images at high resolution - javascript

I'm looking to create a system where users create an image in canvas on their browser (with the help of paper.js or processing.js etc) and be able to print a large (up to A3, 300dpi ideally) size representation of what they have created in canvas.
Obviously exporting images straight from the canvas element in the users browser I'm limited to screen size and resolution etc.
So I've looked into the solution of momentarily scaling up the canvas element when the users saves and capturing the image data at the larger size. This could be a solution but I'm pretty sure file size would get out of hand pretty quickly at larger sizes.
I haven't used Node.js very much but was wondering if anyone with experience would know if Node could achieve this and if it would be a better solution and briefly how I'd go about it?

I see two ways to achieve what you want :
use an oversized canvas, that you scale with css.
For instance, you can have a 1000X1000 canvas, that you show within a 200pxX200px smaller view.
<canvas width=1000 height=1000
style = 'width:200px; height:200px;' id='cv'>
</canvas>
use a small canvas for display on screen, and really draw on a backing canvas, that you reproduce on the view canvas at each change.
Both solution cannot solve the issue that mouse coordinates are integer, so to implement a 'pixel perfect' location of object you'll have to implement some kind of zooming. Second solution might be simpler for this.
To retrieve the mouse coordinates, with css scaling do not forget to multiply them by the scale, and in case 2, by the scale you decided.
// formula to get the css scale :
var cssScaleX = canvas.width / canvas.offsetWidth;
var cssScaleY = canvas.height / canvas.offsetHeight;
// then mouse coords are to be multiplied by the scale :
// mouse.x *= cssScaleX;
I tried quickly both solutions, and i was quite surprised to see that css solution is very slow (in both Ch and FF), and it seems faster to copy a back canvas than to have css doing it.
Maybe it depends on some quality settings, but it seems solution 2 is both more flexible and faster.
first css version is here (move mouse to draw 10X10 rect) :
http://jsbin.com/kegohufu/1/
second back canvas + copy version is here (move mouse to draw 10X10 rect) :
http://jsbin.com/qomiqoqi/1/

Related

How do I determine the resolution of a computer screen in javascript?

I noticed that the <canvas> element can have different scales. For instance, if I set the CSS width and height to 100px, but have the javascript set the element's width and height to 200px, the element is sized down so everything printed on it is 1/2 the size. (Or 2x the resolution)
I have a retina screen Macbookpro, so in development, I set the scaling to 2x so the images and objects look clear and crisp on my screen.
I have heard that other screens can have a 1.2x resolution (CSS pixels vs Actual pixels)
Is there a way to find out what the resolution/scaling is of the device's screen so I can make my canvas as crisp and as clean as possible to the user?
If it helps at all, I'm trying to make a game in javascript using canvas as my graphics output.
These properties will give your dimensions:
window.screen.availHeight
window.screen.availWidth
For pixel depth, use this property:
window.devicePixelRatio
For application in canvas, a helpful script and explanation is given here.
After searching around using different terms, I was able to find the answer that I was looking for.
The window object has a variable called window.devicePixelRatio. This lets us know the ratio of pixels to the device's screen pixels. On my retina screen, this variable gives me a 2. With this, I can set the canvas to the correct scaling so it looks clean and crisp on any screen.
Source: http://www.html5rocks.com/en/tutorials/canvas/hidpi/

Optimizing HTML5 Animation

I have a HTML5 animation that uses a canvas of size 900x520. However ~60% of the canvas is not utilized in the most general case. If some of the parameters of the animation are varied, then the whole canvas is utilized which is the reason why I pegged the maximum canvas size to W:900px and H:520px.
When a user opens the page with this HTML5 canvas, a general case is considered and only 280x280 at the center of the HTML5 canvas is used. As per my knowledge though I'm not using the other 60% of the canvas is not used but is rendered every time which is causing the animation to slow down, especially on tablets and mobile phones (Most of my intended users are tablets / mobile users). So, I would like to know if there are any techniques to render only a set of pixels everytime (say 300x300 at the center of the canvas) ? Are there any workaround solutions ?
I'm already using RequestAnimationFrame (However, I want some more optimization)
First thing i would try is to use the clip() function. It slows down the drawing, but in your case it might be faster to check first against a clip since 60% of the time the clipping will quit the drawing.
Second thing to try is to handle clipping by yourself : handle a viewport object ( center x, center y, width, height ) and exclude unseen object with simple bounding box testing. Rq : Maybe the way you draw your scene allows for easy optimisation.
3) do not define a canvas bigger than the screen : rather define it at max size, and keep track of an offset, like with a viewPort object. And to avoid re-writing all your draw code, just use save/translate/restore before/after your old methods. For the clipping on this canvas, refer to 1) :-)
4) you can reduce graphic strain on mobile by using css scaling. Even if scaling reduces performances, on mobiles it is faster to draw on smaller canvas with a css scale than to draw full resolution with no scale.
So you have to set canvas.style.width and canvas.style.height to some wise values, and you can set canvas.width and canvas.height to the setting that allows for the right performance.

Rescale Image object on Canvas element while maintaining aspect ratio (iOS, Android)

I am trying to load a HTML file that does one thing: display an image on a canvas element that is auto-resized and auto-rescaled to the maximum dimensions for the device in question.
For example:
User presses button on device.
Device opens WebKit/WebView to http://some.url/page.html.
That page shows image.png in the maximum height/width proportion possible on said device.
Requirements:
Actual HTML file should only contain a canvas element within the body.
Image path should be dynamic and the width/height interpreted after the file has been loaded.
No third party dependencies.
I took a stab at this here, but am running into problems: http://jsfiddle.net/zfG2B/
if (scaledHeight < scaledWidth) {
_config.newImgHeight = _cache.canvas.height;
_config.newImgWidth = Math.round(_config.newImgHeight * _config.imgWidthRatio);
} else if (scaledWidth < scaledHeight) {
_config.newImgWidth = _cache.canvas.width;
_config.newImgHeight = Math.round(_config.newImgWidth * _config.imgHeightRatio);
}
The code I've cobbled together (above, and see jsfiddle) kind of works, but I can only get it to rescale based on the height. Any ideas on what I've done wrong or the best path forward?
For reference, I've attempted to join together the best of:
HTML5 Canvas Resize (Downscale) Image High Quality?
Scaling HTML5 canvas width preserving w/h aspect ratio
Resizing an image in an HTML5 canvas
HTML5 Canvas drawImage ratio bug iOS
scaling a canvas element and keeping the aspect ratio
So here's the question: what's the best way to place an Image on a Canvas in HTML5 and make it auto-rescale based on the current screen size? Bonus points for being able to specify a clipping range!

Google Chrome - context.canvas.width/context.canvas.width causes "Blue layer"

I upgraded from Chrome version 17 to Chrome: 18.0.1025.142 today.
I've noticed one of the web apps I am working on has a strange blue layer that appears on load and then vanishes when you start scrolling.
I've tracked this down to the following lines in my JS:
context.canvas.width = canWidth;
context.canvas.height = canHeight;
canWidth and canHeight are generated dynamically.
Commenting these lines out stops the blue layer from rendering, however this isn't a fix as I need to use the dynamically generated values to control the canvas width/height.
I also attempted hard coding the context.canvas.width and context.canvas.height to 600 and this also generated the blue layer issue.
This wasn't a problem on previous versions of Chrome, and I don't have any problems in FireFox.
Any suggestions on what the issue might be?
Edit:
Here is the block of code where the above resides (nodeLeft and nodeTop are generated else where):
context.clearRect ( 0 , 0 ,canWidth, canHeight );
context.canvas.width = canWidth;
context.canvas.height = canHeight;
context.beginPath();
context.moveTo(x, y);
context.lineTo(nodeLeft, nodeTop);
context.strokeStyle = "#000000";
context.lineCap = "round";
context.stroke();
Can you post more code than the two lines you've provided? I believe it's something more than where you've set the width/height.
I have a drawing application that I've been tinkering with on and off for a while in which I do very much the same thing. The only difference is that I call context.scale(0,0); before setting the width and height. I have no issues in any version of Google Chrome with a blue overlay. Are you possibly modifying the context's scale value before setting the width and height?
e.g. here's what I have, setting the canvas to document width/height for a full-document drawing area:
context.scale(0,0);
context.canvas.width = $(document).width();
context.canvas.height = $(document).height();
edit: my tinkering site mentioned is at http://drawcomix.com if you want to test the blue overlay to see if it's your browser or your code.
edit: based on your additional comment...
I've seen tutorials on the web that suggest the best way to clear the canvas is to set the height/width. That's not entirely true. On document load, it may be the quickest/easiest way to do so, but on mouse movements, you're probably better off doing something like this:
context.save();
context.setTransform(1,0,0,1,0,0);
context.clearRect(0,0,width,height); // width/height of canvas
context.restore();
You may not need setTransform if you're not performing any transformations on the canvas.
I believe I found this solution in Pro HTML5 Programming (Apress), but I'm not sure.
EDIT:
Ok, I got it for me. It seems to be a problem with some AMD graphic cards. I could turn off the high performance mode for chrome in the catalyst control center. That fixed most of the annoying things for me...hope that it'll be fixed in the next versions.
For mor information see: http://code.google.com/p/chromium/issues/detail?id=121658
This is not a solution, but I'm facing the same problem and it's driving me nuts. I'm trying to nail it down. I've tested differnt browser on different machines:
Problems:
Blue layers (I only see them if Chrome is NOT maximized)
Misplaced y scrollbar (sometimes only shows up if I switch through the tabs)
Distorted shapes. The size of the canvas and the drawed images / shapes vary from chrome to other browser. Means, the canvas / shapes is smaller on Chrome, it mostly shrinks the width.
Conclusion 1:
It seems that it does not happen on every machine with Chrome 18.x.x. Seems to be a hardware issue, but couldn't constrain it futher actually.
Conclusion 2:
If i set the size of the canvas to 200 height/width, the problems don't appear. If I set it higher than 200, it will deform the canvas / rectangle and other problems appear.
Conclusion 3:
The problems show only up if something is drawed to the canvas or something is set, i.e. fillStyle.
Here the test snippets I used:
<canvas id="canvas" width="300" height="300" style="background-color:orange"></canvas>
...
ctx.fillRect (10, 10, 55, 50);

Scrolling huge backdrop in canvas

My problem is the company I'm working for has started giving me art for the game I'm making for them and it has a huge world! It has given me a huge backdrop image like 12923x5356 huge!
I've now been able to get it to scroll and work like a backdrop but it's a bit slow on my old computer that's an old thinkcenter (note it can only run crunchbang and barely xp). See we want to be able to support tablets for this. It also further complicates things that they have an overlay as well just as big.
Right now I have it only drawing a canvas sized chunk of the backdrop so it only draws as much as can be seen by using
ctx.drawImage(img, x, y, width, height, dx, dy, dwidth, dheight);
with the dx/dy always drawing to the same spot and x/y scrolling with the main character.
Now realistically, is there any way to possibly do this faster? Another way I can think of is to maybe break the image down to small chunks with getImageData and drawing just so many that fills the screen with ctx.putImageData(img, x, y). I'm pretty sure from experience that the small form of drawImage is a bit faster than the long form but would this be much faster?
Anyone else have any ideas they can shoot at me?
You can make it into smaller tiles, just like how Google Maps does.
I'm not sure if you're handling some huge game map, but the technique should be the same. You can split the map into small tiles of, for example, 256px * 256px. Then you only need several 256px * 256px div to cover the viewport. If you use canvas instead, it's the same. Just render those tiles which are currently shown to the user.

Categories

Resources