How to draw a 1cm wide square in html5 canvas? - html

Drawing a black square on white background is easy using canvas API.
Nevertheless, a lot of devices have different window.devicePixelRatio values, but even if they do, the physical size of a pixel might differ.
Can the needed scaling factor be retrieved using HTML5 APIs?
If not, how it can be determined using more low level host OS APIs (lets say, linux, windows, android, ios, osx ...

There is really no way to know how large 1 cm would be on the screen. The browser has just a generic concept of the DPI for the screen which is typically 96 DPI. However, you could be sitting by a 19" or a 32" monitor - the browser wouldn't know and 1 cm would be very different in size between them assuming they are defined by the same number of pixels.
You will have to manually calibrate for each monitor by physically measuring the size and then convert this to number of pixels per cm or inch when it comes to browsers.
Old-tech method
I made this simple tool (see below) to do basic calibration - it won't give you a scientifically exact result, but a close enough result. And yes, you would have to put a physical ruler onto the screen to measure and adjust the slider so that the lines matches up with the ruler. This is of course not so practical to ask users to do, which is a drawback... (it can be modified to take a known object of a known size instead). Separate X/Y as needed.
When you then have the DPI (or PPI) for your screen, you can use that as a scale factor for anything that goes onto the screen, as well as its inverse (1/ppi) for the other direction, from screen to path etc.
The scale factor is calculated dividing the result on 96.
var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
rng = document.querySelector("input");
ctx.translate(0.5, 0.5);
ctx.font = "16px sans-serif";
ctx.fillStyle = "#c00";
(rng.oninput = render)();
function render() {
var v = +rng.value, sepDist = 4; // 4"
ctx.clearRect(-0.5, -0.5, c.width, c.height);
ctx.strokeRect(0, 0, v, v); // draw 1:1 pixels:ppi
ctx.fillText(v + " PPI", 10, 20);
// draw marks which should be 4 inches apart
ctx.fillRect(0, 0, 3, 140);
ctx.fillRect(96 * sepDist * (v / 96), 0, 3, 140); // assuming 96 DPI base resolution
ctx.fillText("------ Should be 4 inches apart ------", 50, 140);
ctx.fillText((v / 96).toFixed(2) + "x (96 DPI)", 10, 40);
}
<label>Adjust so square below equals 1x1 inch:
<input type=range value=96 min=72 max=150></label>
<canvas width=630 height=300></canvas>

Related

Line quality is very low with jagged or blurry edges

I'm working on a drawing app and the line quality seems to be very low and jagged compared to other drawing apps.
Or it might be that other apps are doing something different than I'm doing.
What I have done so far is use the graphics property to draw the lines. I also collect the mouse positions on the mouse move events for assigning to a path later on. Summarized it:
MouseDownHandler:
mouseDownPoint.x = event.stageX;
mouseDownPoint.y = event.stageY;
drawCommands.push(GraphicsPathCommand.MOVE_TO);
simplePath = "M " + mouseDownPoint.x + " " + mouseDownPoint.y;
MouseMoveHandler:
line.graphics.lineStyle(lineWeight, lineColor, lineAlpha, pixelHinting);
line.graphics.moveTo(previousPoint.x, previousPoint.y);
scaledPoint = new Point(localPoint.x/scaleX, localPoint.y/scaleY);
line.graphics.lineTo(scaledPoint.x, scaledPoint.y);
previousPoint.x = scaledPoint.x;
previousPoint.y = scaledPoint.y;
simplePath += " L " + scaledPoint.x + " " + scaledPoint.y;
MouseUpHandler:
myPath.data = simplePath;
As I draw I update the line (which is a UIComponent but could just as well be a Shape or Sprite - anything with a graphics property). At the same time I keep track of the mouse locations in the simplePath string.
When the mouse is up I clear the line graphics and show a path graphic element. The Path isn't important to this but I noticed it looks slightly cleaner than the line that had been drawn. That might be because it has pixel hinting (it's not much cleaner). Sometimes there are artifacts. And I'm including it in case I need to use a path for some reason.
Here is the screen shot:
The pixel hinted version looks crisper but it still is far below the quality of the line drawing in other apps and in some cases it makes it look more jagged. Is there something I'm missing?
Note: I included graphics2d and canvas2d because I believe this may not be related to the specific language or platform but might be related to drawing graphics in general.
The green line is produced by Graphics.cubicCurveTo(...) method. Initially you have a list of user-provided points A1,A2,A3...An. In order to use cubic curves you also need to figure 2 control points CFk (forward) and CBk (backward), for each Ak respectively, so you draw that big curve starting from A1 and every curve piece from Ak-1 to Ak will take arguments .cubicCurveTo(CFk-1, CBk, Ak);
For each Ak (except for A1 and An) you can calculate CFk and CBk as following:
(vector)AForward = (vector)(Ak+1 - Ak-1)
(vector)AForward.length = (vector)(Ak+1 - Ak).length / 3
CFk = Ak + (point)AForward
(vector)ABackward = (vector)(Ak-1 - Ak+1)
(vector)ABackward.length = (vector)(Ak-1 - Ak).length / 3
CBk = Ak + (point)ABackward
Then, there are A1 and An that are left out, but I am sure you can figure them on your own.
For vector math you can use ru.delimiter.math.Vector2D class (works with both Cartesian and Polar coordinates) from my small collection of useful things: https://bitbucket.org/thydmitry/ru.delimiter/src/9083fb46ce1c/classes/ru/delimiter/math/
P.S. Maybe you don't need to go that extreme and will be fine with the red line, that is a simple .curveTo(Ak, (Ak + Ak+1)/2);
UPD: a simple algorithm to inscribe a curve into zigzag provided by an array of points.
function middle(A:Point, B:Point):Point
{
return new Point((A.x + B.x) / 2, (A.y + B.y) / 2);
}
function drawTo(target:Point):void
{
graphics.lineTo(target.x, target.y);
}
function bendTo(control:Point, target:Point):void
{
graphics.curveTo(control.x, control.y, target.x, target.y);
}
// This should contain at least 2 points before you start drawing.
var PP:Vector.<Point>;
// Go to the start position.
graphics.lineStyle(0, 0xFF0000);
graphics.moveTo(PP[0].x, PP[0].y);
// Draw a straight line to the center of the first zigzag segment.
drawTo(middle(PP[0], PP[1]));
// For each 3 consequent points A,B and C, connect
// the middle of AB and the middle of BC with a curve.
for (var i:int = 2; i < PP.length; i++)
{
bendTo(PP[i - 1], middle(PP[i - 1], PP[i]));
}
// Connect the center of the last zigzag segment with the end point.
drawTo(PP[PP.length - 1]);
There are multiple reasons:
Stage quality. In Flash Player you can set the stage quality to LOW, MEDIUM, HIGH, BEST, 8x8, 8x8Linear, 16x16 and 16x16Linear. This affects if there is antialiasing applied on lines / paths and how many times it's applied. Increasing the quality helps but in 8x8 and higher quality there are bugs in the Flash Player (font size reduced by 25% on non-embedded fonts, graphics artifacts, gradient fills color count reduced).
Pixel snapping. If you have a 1px line that ends up positioned on a half pixel it is anti aliased over two lines. Normally antialiasing increases quality but in the case of an offset single pixel line it reduces quality. Setting pixel snapping helps.
Using curveTo and cubicCurveTo instead of line points as #Organis suggested. Not sure how to do this yet.
Will try to post images of the difference in each case when I get a chance.

Preffered viewPort to dealing with textButton libgdx

i work with a new game , i have create 2 camera one camera for real world and another (GUI) camera for a static resolution (800 x 480)
private void init () {
camera = new OrthographicCamera(16,9);
camera.position.set(0, 0, 0);
camera.update();
cameraGUI = new OrthographicCamera(800,480);
cameraGUI.position.set(0, 0, 0);
cameraGUI.update();
And i want to use a textButton in my game , so i've create a new stage using a new fitViewPort
fitViewPort=new FitViewport(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
gameController.stage=new Stage(fitViewPort);
then i create my textButtons using my bitmap font and i try it on the Desktop , and it's great
desktop
but when i try another device , the position is not the same
Tablet
then i think for using the cameraGui Width and height for my view port , the font is very bad stretched
any help please :(
Don't create a special camera for GUI, because viewport already has a camera.
Make your viewport width and height fixed, in your case:
fitViewPort = new FitViewport(800, 480);
gameController.stage = new Stage(fitViewPort);
And then just add actors to stage and draw it just by calling method
gameController.stage.draw();
Note: Remember to add gameController.stage.getViewport().update(width, height); in your resize method.
Edit: Using small fixed viewport (800x480 is small) will cause scaling, that is why the font is stretched. For example if you run a 800x480 layout on 1920x1080 display it will be scaled by 225%.
What you can do is generate a font with big size and the set scale to its data fe. bitmapFont20.getData().setScale(0.5F); (in this case font is generated by FreeTypeFontGenerator with size 40). Using this trick you will be able to get a great results. The proof is below. It is viewport of 640x360F * (float) Gdx.graphics.getHeight() / (float) Gdx.graphics.getWidth() scaled to 1920x1080. Font is generated with size 72 and then data scale is set to 0.5F
Here is the same text, but without modified data scale.

HTML5 Canvas save() and restore() performance

So the issue that I'm having is that in developing an HTML5 canvas app I need to use a lot of transformations (i.e. translate, rotate, scale) and therefore a lot of calls being made to context.save() and context.restore(). The performance drops very quickly even with drawing very little (because the save() and restore() are being called as many times as possible in the loop). Is there an alternative to using these methods but still be able to use the transformations? Thank you!
Animation and Game performance tips.
Avoid save restore
Use setTransform as that will negate the need for save and restore.
There are many reasons that save an restore will slow things down and these are dependent on the current GPU && 2D context state. If you have the current fill and/or stroke styles set to a large pattern, or you have a complex font / gradient, or you are using filters (if available) then the save and restore process can take longer than rendering the image.
When writing for animations and games performance is everything, for me it is about sprite counts. The more sprites I can draw per frame (60th second) the more FX I can add, the more detailed the environment, and the better the game.
I leave the state open ended, that is I do not keep a detailed track of the current 2D context state. This way I never have to use save and restore.
ctx.setTransform rather than ctx.transform
Because the transforms functions transform, rotate, scale, translate multiply the current transform, they are seldom used, as i do not know what the transform state is.
To deal with the unknown I use setTransform that completely replaces the current transformation matrix. This also allows me to set the scale and translation in one call without needing to know what the current state is.
ctx.setTransform(scaleX,0,0,scaleY,posX,posY); // scale and translate in one call
I could also add the rotation but the javascript code to find the x,y axis vectors (the first 4 numbers in setTransform) is slower than rotate.
Sprites and rendering them
Below is an expanded sprite function. It draws a sprite from a sprite sheet, the sprite has x & y scale, position, and center, and as I always use alpha so set alpha as well
// image is the image. Must have an array of sprites
// image.sprites = [{x:0,y:0,w:10,h:10},{x:20,y:0,w:30,h:40},....]
// where the position and size of each sprite is kept
// spriteInd is the index of the sprite
// x,y position on sprite center
// cx,cy location of sprite center (I also have that in the sprite list for some situations)
// sx,sy x and y scales
// r rotation in radians
// a alpha value
function drawSprite(image, spriteInd, x, y, cx, cy, sx, sy, r, a){
var spr = image.sprites[spriteInd];
var w = spr.w;
var h = spr.h;
ctx.setTransform(sx,0,0,sy,x,y); // set scale and position
ctx.rotate(r);
ctx.globalAlpha = a;
ctx.drawImage(image,spr.x,spr.y,w,h,-cx,-cy,w,h); // render the subimage
}
On just an average machine you can render 1000 +sprites at full frame rate with that function. On Firefox (at time of writing) I am getting 2000+ for that function (sprites are randomly selected sprites from a 1024 by 2048 sprite sheet) max sprite size 256 * 256
But I have well over 15 such functions, each with the minimum functionality to do what I want. If it is never rotated, or scaled (ie for UI) then
function drawSprite(image, spriteInd, x, y, a){
var spr = image.sprites[spriteInd];
var w = spr.w;
var h = spr.h;
ctx.setTransform(1,0,0,1,x,y); // set scale and position
ctx.globalAlpha = a;
ctx.drawImage(image,spr.x,spr.y,w,h,0,0,w,h); // render the subimage
}
Or the simplest play sprite, particle, bullets, etc
function drawSprite(image, spriteInd, x, y,s,r,a){
var spr = image.sprites[spriteInd];
var w = spr.w;
var h = spr.h;
ctx.setTransform(s,0,0,s,x,y); // set scale and position
ctx.rotate(r);
ctx.globalAlpha = a;
ctx.drawImage(image,spr.x,spr.y,w,h,-w/2,-h/2,w,h); // render the subimage
}
if it is a background image
function drawSprite(image){
var s = Math.max(image.width / canvasWidth, image.height / canvasHeight); // canvasWidth and height are globals
ctx.setTransform(s,0,0,s,0,0); // set scale and position
ctx.globalAlpha = 1;
ctx.drawImage(image,0,0); // render the subimage
}
It is common that the playfield can be zoomed, panned, and rotated. For this I maintain a closure transform state (all globals above are closed over variables and part of the render object)
// all coords are relative to the global transfrom
function drawGlobalSprite(image, spriteInd, x, y, cx, cy, sx, sy, r, a){
var spr = image.sprites[spriteInd];
var w = spr.w;
var h = spr.h;
// m1 to m6 are the global transform
ctx.setTransform(m1,m2,m3,m4,m5,m6); // set playfield
ctx.transform(sx,0,0,sy,x,y); // set scale and position
ctx.rotate(r);
ctx.globalAlpha = a * globalAlpha; (a real global alpha)
ctx.drawImage(image,spr.x,spr.y,w,h,-cx,-cy,w,h); // render the subimage
}
All the above are about as fast as you can get for practical game sprite rendering.
General tips
Never use any of the vector type rendering methods (unless you have the spare frame time) like, fill, stroke, filltext, arc, rect, moveTo, lineTo as they are an instant slowdown. If you need to render text create a offscreen canvas, render once to that, and display as a sprite or image.
Image sizes and GPU RAM
When creating content, always use the power rule for image sizes. GPU handle images in sizes that are powers of 2. (2,4,8,16,32,64,128....) so the width and height have to be a power of two. ie 1024 by 512, or 2048 by 128 are good sizes.
When you do not use these sizes the 2D context does not care, what it does is expand the image to fit the closest power. So if I have an image that is 300 by 300 to fit that on the GPU the image has to be expanded to the closest power, which is 512 by 512. So the actual memory footprint is over 2.5 times greater than the pixels you are able to display. When the GPU runs out of local memory it will start switching memory from mainboard RAM, when this happens your frame rate drops to unusable.
Ensuring that you size images so that you do not waste RAM will mean you can pack a lot more into you game before you hit the RAM wall (which for smaller devices is not much at all).
GC is a major frame theef
One last optimisation is to make sure that the GC (garbage collector) has little to nothing to do. With in the main loop, avoid using new (reuse and object rather than dereference it and create another), avoid pushing and popping from arrays (keep their lengths from falling) keep a separate count of active items. Create a custom iterator and push functions that are item context aware (know if an array item is active or not). When you push you don't push a new item unless there are no inactive items, when an item becomes inactive, leave it in the array and use it later if one is needed.
There is a simple strategy that I call a fast stack that is beyond the scope of this answer but can handle 1000s of transient (short lived) gameobjects with ZERO GC load. Some of the better game engines use a similar approch (pool arrays that provide a pool of inactive items).
GC should be less than 5% of your game activity, if not you need to find where you are needlessly creating and dereferencing.

libgdx Shaperenderer line .. How to draw line with a specific width

I am trying to draw a line of specific width using libgdx shape renderer. I followed this link
The problem is if i specify more line width i.e more than 9 it does not show increased width. More than 9 either i specify 20 or 100 pixels it will have the same result as 9
shapeRenderer.begin(ShapeType.Line);
shapeRenderer.line(50, 70, 0, 50, 200, 0, Color.BLUE, Color.RED);
int lineWidth = 20; // pixels
Gdx.gl10.glLineWidth(lineWidth / camera.zoom);
shapeRenderer.end();
Thanks
Shakeel
To query the range of supported widths and the size difference between
supported widths within the range, call glGet with arguments
GL_ALIASED_LINE_WIDTH_RANGE, GL_SMOOTH_LINE_WIDTH_RANGE, and
GL_SMOOTH_LINE_WIDTH_GRANULARITY.
Reference
To avoid device specific behavior, I use a quad instead. Draw a 1x1 square from a small texture and then position, scale (depending on the width and height of the line you wanted to draw), color, and rotate it.
You should use ShapaRenderer.rectLine. You can see a more detailed answer here
Use this
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
instead of shapeRenderer.begin(ShapeType.Line);
Replace 10 with line width you want
Gdx.gl.glLineWidth(10 / camera.zoom);

WebGL: canvas coordinates to 3d coords

I am trying to find the "left" border of my WebGL view port because I would like to draw a number of debug information there. (an axis mini map like most modeling programs have)
I certainly can get the width and height of the canvas containing the WebGL viewport.
I would really like to know how I would go about calculating 2d canvas coordinates to 3d coordinates? What would be the best approach to find the left border in the 3d viewport?
Anyone looking into this should read
http://webglfactory.blogspot.com/2011/05/how-to-convert-world-to-screen.html or take a look at GluProject() and GluUnproject()
To clarify datenwolf's answer, the coordinate mapping between your 3D space and 2D canvas is exactly what you want it to be. You control it with gl.viewport and the matrices that you pass to your shader.
gl.viewport simply blocks out a rectangle of pixels on your canvas that you are drawing to. Most of the time this matches the dimensions of your canvas exactly, but there are some scenarios where you only want to draw to part of it. (Split-screen gaming, for example.) The area of your canvas that you're drawing to will be referred to as the viewport from here on out. You can assume it means the same thing as "canvas" if you'd like.
At it's simplest, the viewport always has an implicit coordinate system from -1 to 1 on both the X and Y axis. This is the space that the gl_Position output by your vertex shader operates in. If you output a vertex at (-1, -1) it will be in the bottom left corner of your viewport. a vertex at (1, 1) will be in the top right. (Yes, I'm ignoring depth for now) Using this, you could construct geometry designed to map to that space and draw it without any matrix transforms at all, but that can be a bit awkward.
To make life easier, we use projection matrices. A projection matrix is simply one that transforms your geometry from some arbitrary 3D space into that -1 to 1 space required by the viewport. The most common one is a perspective matrix. How you create it will look a bit different depending on the library you use, but typically it's something like this:
var fov = 45;
var aspectRatio = canvas.width/canvas.height;
var near = 1.0;
var far = 1024.0;
var projectionMat = mat4.perspective(fov, aspect, near, far);
I'm not going to get into what all those values mean, but you can clearly see that we're using the canvas width and height to help set up this projection. That allows it to not look stretched or squashed depending on the canvas size. What it all boils down to, however, is that taking any 3D point in space and multiplying it by this matrix will produce a point that maps to that -1 to 1 space, taking into account distance from the 'camera' and everything else. (It may actually fall outside of that bounds, but that simply means it's off camera.) It's what makes our 3D scenes look 3D.
It's also possible to create an projection matrix specifically for drawing 2D geometry, though. This is called an orthographic matrix, and the setup typically looks something like this:
var left = 0;
var top = 0;
var right = canvas.width;
var bottom = canvas.height;
var near = 1.0;
var far = 1024.0;
var projectionMat = mat4.ortho(left, right, bottom, top, near, far);
This matrix is different than the perspective matrix in that it ignores the z component of your position entirely. Instead, this matrix transforms flat coordinates, like pixels, into the -1 to 1 range. As such, your scenes don't look 3D but it's easier to control exacty where things appear on screen. So, using the matrix above, if we give it a vertex at (16, 16, 0) it will appear at (16, 16) on our canvas (assuming the viewport is the same dimensions as the canvas). As such, when you want to draw things like flat UI elements this is the type of matrix you want!
The nice part is that because these are just values that you pass to a shader you can use completely different matrices from one draw call to the next. Typically you'll draw all of your 3D geometry with a perspective matrix, then all of your UI with a orthographic matrix.
Apologies if that was a bit rambling. I've never been terribly good at explaining all that math-y stuff.
I am trying to find the "left" border of my WebGL view port because I would like to draw a number of debug information there. (an axis mini map like most modeling programs have) I certainly can get the width and height of the canvas containing the WebGL viewport.
Just switch the viewport and projection for those parts. You can change them anytime.
See http://games.greggman.com/game/webgl-fundamentals
Basically if you want to draw in 2D use a 2D shader, don't try messing with a 3D shader.
WebGL draws in clipspace so all you need to do is convert from pixels to clip space.
attribute vec2 a_position;
uniform vec2 u_resolution;
void main() {
// convert positions from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
Why not just use some overlaid HTML?
<html>
<head>
<style>
#container {
position: relative;
}
#debugInfo {
position: absolute;
top: 0px;
left: 0px;
background-color: rgba(0,0,0,0.7);
padding: 1em;
z-index: 2;
color: white;
}
</style>
</head>
<body>
<div id="container">
<canvas></canvas>
<div id="debugInfo">There be info here!</div>
</div>
</body>
</html>
You can then update it with
var debugInfo = document.getElementById("debugInfo");
debugInfo.innerHTML = "some info";