I am experimenting with html5 JavaScript game engines and Cordova.
Often I encounter key words like aspect ratio and scaling.
Since html5 games deals with canvas and game images are put through the drawImage() i fail to understand the need for aspect ratio while building the games with Cordova.
Is there any good example to understand aspect ratio or scaling?
Aspect ratio
Aspect ratio is what you get when you divide an image's width by the height. It can be written as a single number (2), or as a fraction (2/1) or with a colon 2:1.
A perfect square has an aspex of 1 or 1:1, its height is the same as its width.
If you have a widescreen monitor it will have an aspect of 16:10 or 1.6, your iphone 3:2 or 1.5 and an old TV has 4:3 or 1.3. The closer the number gets to 1 the squarer the image/screen/thing is.
The aspect ratio is size and unit invariant. That means that no matter how large the object is, or what units its measured in, feet, meters, inches or millimeters the aspect is always the same.
If you have the aspect then all you need is the length of one side and you can work out the length of the other size. So if you had a wide screen TV aspect ratio 16:9 or 1.7 and it was as tall as you then you also know that it will be 1.7 times as wide as you if you layed down beside it.
When you resize an image you have to know its aspect ratio or you will squash or stretch the image. For example if you take a widescreen movie (16:9 or 1.7) and make it fit on a old TV (4:3 or 1.3) because the aspect has changed everything will get squashed and look skinny.
Aspect ratio in code.
In javascript an image has a width and height property expressed as pixels. To work our the aspect ratio is easy
var aspect = image.width / image.height;
If you want to make that image fit a region that is 1000 pixels high. You know that the width will be equal to the height times the aspect.
ctx.drawImage(image, 0, 0, 1000 * aspect, 1000);
If you want to fit a 1000 pixel width then you divide the width by the aspect to get the height.
ctx.drawImage(image, 0, 0, 1000, 1000/aspect);
It wont get squashed, circles will still be circles, squares will still be square and skinny people will still look skinny.
A famous aspect ratio is the golden ratio 1.618 or 16:10. It is said to be the most pleasing to look at. Many famous paintings have that aspect from way back, as well as building, sculptures, landscapes and more.
Another well know one, and a very handy aspect ratio is the square root of 2 : 1. it is the aspect ratio of the A0, A1, A2 .. paper and has a property no other aspect ratio has. If you take a A0 sheet of paper and fold it in half then turn it on its side the aspect of the folded sheet is still square root of 2 : 1. Its size is now A1, fold again to get A2, then A3... No mater how often you fold it the aspect still stays the same. It is the only aspect ratio that has that property.
Hope that helped explain what aspects ratios are, what they are used for, and how to use them..
Related
This question already has answers here:
Display pixel-perfect canvas on all devices
(3 answers)
Closed last month.
Is it possible to set the size of the bitmap in an HTML5 canvas so that it exactly matches the display size in device pixels, ie the number of physical pixels used to display the canvas, even when window.devicePixelRatio is not an integer?
There is a good description of how to resize the canvas on webglfundamentals but it does not properly handle non-integer devicePixelRatios. My understanding so far:
The display size of the canvas is set in CSS with, eg, canvas {width: 200px;}.
The underlying bitmap size is set with, eg, <canvas width="100"/> in HTML or canvas.width = 100 in JS.
The bitmap will be stretched to fit the CSS size (how is affected by object-fit and object-position).
We can set bitmap size to be equal to some expression involving canvas.clientWidth.
canvas.clientWidth is an integer and its unit are CSS pixels and is the calculated width of the content (plus padding). I don't know if the browsers actually draw the content into a whole number of CSS pixels or a whole number of device pixels.
So webglfundamentals suggests something like
canvas.width = Math.floor(canvas.clientWidth * window.devicePixelRatio);
but if window.devicePixelRatio is a fraction, this sometimes doesn't work (2 pixel wide lines drawn on integer coordinates are fuzzy). My 1920x1080 screen has devicePixelRatio of 1.5 by default, and page zoom can affects this, so lots of reasons why devicePixelRatio is not an integer as a rule. What can we do?
1px is a logical length unit in CSS/DOM realm. Dot.
So I don't think that you will find reliable method for getting physical pixels. Check this.
That's why in Sciter I've made px units to represent always physical pixels of the device. And introduced dip units that are logical pixels - 1/96 of inch measured by the ruler on device surface (so 1dip ~ 1px of W3C CSS).
I simply do not understand how to do serious UI development without the ability to define objects in device pixels.
I know this is an old thread, but I hope I can help a bit any other wanderer coming to this.
There is a way to make a "canvas pixel" match "device pixel" as close to a 1:1 ratio as possible, at least from my experience while making a Canvas WebGL based game engine, even with non-integer devicePixelRatio values.
First, there are 3 things we need to understand:
CSS Size: Refers to the actual Canvas DOM element size in CSS pixel (px) units.
Canvas Size: Logical size of the canvas, as set using the element properties (i.e. canvasElem.width = 100;), this affects your render space.
Device Pixel Ratio: Value obtained from window which effectively describes the ratio between a physical pixel and a CSS pixel.
Brief Example
Let's say you have a devicePixelRatio of 2.625, and your mobile device reports a screen/window size of 411 x 487 -a weird size- you might say, but that is in CSS pixel units, if we multiply those by the devicePixelRatio we get a size of 1078 x 1278 which tells us that most likely the device screen is actually a 1080 x 1280 screen, as originally advertised by your favorite phone manufacturer.
Now, to size stuff...
Say we want a canvas filling the whole screen. Set your CSS Size match the screen size (or any box size you want).
canvas.style.width = '411px';
canvas.style.height = '487px';
Now set the Canvas Size to the same but scaled-up by devicePixelRatio:
canvas.width = 411 * window.devicePixelRatio;
canvas.height = 487 * window.devicePixelRatio;
Now you can render your images.
When the browser calculates the layouts, it will have to scale-down your canvas to make it fit in its DOM rectangle area, the down-scale factor is cssSize / canvasSize yielding a down-scale factor of 1 / devicePixelRatio.
And then when the browser needs to render the layout to physical screen, it will scale it up by devicePixelRatio, thus leaving a total scaling of 1.0.
TL;DR
Supersampling. Scale up your canvas logical size by devicePixelRatio while keeping your CSS size at whatever size you need, and let the browser do the rest.
NOTE: If your rendering expects the canvas to be of certain size, and this "scaling up" messes up your layout and/or calculations, use the canvas setTransform method to scale down your "logical units" by 1.0 / devicePixelRatio, no quality is loss at all.
I'm implementing a graphic related web app and I want to detect the image resolution and validate if the user is uploading an image with too low DPI
How can I achieve this?
Is there a library for this?
Is there a solution in HTML 5 canvas for this, or in Konvajs (HTML canvas library)
Is there any image up-loader that supports determining uploaded image resolution?
Most modern file uploaders will give you access to image dimensions, or you can use a filereader to load the image into a hidden dom image object and get the sizes that way. Example in this SO question.
But they will not tell you if the image is suitable for your intended use. You have to do some math yourself to decide that.
To decide if an image is going to be acceptable, we need to start with the target. We need to know the final output DPI and the measured dimensions. I will look at the width only but the same calculations work for the height too. Example DPI's, for a PC screen the DPI is 96, for a decent inkjet printer it might be up to 4800 by 1200. Commercial printing ranges from 300 to 2400.
Next we need to know the target region size - in other words a measurement. I will use inches here to keep things simple.
The calculation we need to do is:
Image size dots / (target size inches * DPI)
This produces the image scaling that is needed to fit the image in the target space. If the image dot size is twice the size of the target then the scale is 0.5, if the image dot size is 3 times the target then the scale is 3.
Next we need to know the acceptable range of scaling for the image. This is arbitrary and depends on the circumstances in hand.
Let's take an example - making a business card 3.5 ins x 2 ins where we want to put an uploaded picture on the back. We are printing at 300 DPI. The user uploads an image that is 800px wide. I will ignore aspect ratio to keep it simple.
The calculation we need to complete is -
Perfect image dot size would be: 3.5 ins x 300 DPI = 1050 dots.
Scaling of image to target = 800 / 1050 = 0.76
Assume acceptable scale range of 0.8x - 4x.
Conclusion: The image is not suitable because the scale from step 2 is outside the range defined in step 3.
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/
var winsize = "Window size " + screen.width;
It would say 860 pixels when my phone resolution is 1440 x 2560 pixels even if browser is taking the whole screen rotated or not
Mobile devices have viewport scaling. They display pages in virtual pixel dimensions so that pages which use pixel values for sizing will still look reasonable on small displays.
Mobile devices tend to have extremely high pixel densities and without this viewport scaling, most of the web would be unreadable and unusable.
There are other Stack Overflow questions if you want to know how to deal with this issue:
Get the browser viewport dimensions with JavaScript
I'd like to get the aspect ratio of a YouTube video, to resize the player accordingly. I'm programming the YT player using JavaScript.
I would suggest hitting the oembed url:
https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={videoID}&format=json
This gives you the exact video dimensions for videos that are public. I'm not sure about private videos though. It will also return thumbnail dimensions, which seem to be different im some cases, so just be sure to not mix them up.
The only place that exact video dimensions are exposed in a Data API call is when you make a videos.list(part=fileDetails, id=VIDEO_ID) call using the v3 API, while authenticated as the owner of the video. It's returned in the video.fileDetails.videoStreams[].aspectRatio property. This isn't particularly useful, since you need to be authenticated as the video's owner in order to get that info.
If you just have a webpage, and want to make a JSONP call to get a hint about whether a given video is 16:9 or 4:3, you can do that via something like
http://gdata.youtube.com/feeds/api/videos/VIDEO_ID?v=2&alt=jsonc&callback=myCallback
E.g.
http://gdata.youtube.com/feeds/api/videos/F1IVb2_FYxQ?v=2&alt=jsonc&callback=myCallback
has "aspectRatio":"widescreen" set in its response, which is a hint that the video is 16:9 (or close to 16:9).
http://gdata.youtube.com/feeds/api/videos/u1zgFlCw8Aw?v=2&alt=jsonc&callback=myCallback
does not have aspectRatio set at all, which means that the videos is 4:3 (or close to 4:3). It's not always the exact aspect ration, but it's close enough for the vast majority of videos to be useful.
Here is how I do it. I get the aspect ratio from the youtube image.
<img id"nnS7G3Y-IDc-img" src="http://i.ytimg.com/vi/nnS7G3Y-IDc/default.jpg" />
<script>
//using jquery
var height = $('#nnS7G3Y-IDc-img').css('height');
var width = $('#nnS7G3Y-IDc-img').css('width');
height = height.replace('px', '');
width = width.replace('px', '');
var arB = height / 3;
var arT = width / arB;
if (arT == 4) {
//do what you need to with the aspect ratio info from here
//just demonstrating with an alert
alert ("4:3");
}
else {alert ("16:9");}
</script>
I pull all the video information from the youtube api and then store all the video information in a database beforehand, so if you are doing this on the fly, you might have to hide the image on the page and then get the aspect ratio that way.
edit** Another option, and probably the best, would be to use youtube's api.
Search for a video, and check if the data->items->aspectRatio is set. I don't think it's set on 4:3 video, but on 16:9 it is set to widescreen. Should be as simple as if (data->items->aspectRatio) {ratio= "16:9"} else {ratio="4:3"}
Aspect ratio apparently depends on the quality level. Taken from the YouTube Docs:
Quality level small: Player height is 240px, and player dimensions are at least 320px by 240px for 4:3 aspect ratio.
Quality level medium: Player height is 360px, and player dimensions are 640px by 360px (for 16:9 aspect ratio) or 480px by 360px (for 4:3 aspect ratio).
Quality level large: Player height is 480px, and player dimensions are 853px by 480px (for 16:9 aspect ratio) or 640px by 480px (for 4:3 aspect ratio).
Quality level hd720: Player height is 720px, and player dimensions are 1280px by 720px (for 16:9 aspect ratio) or 960px by 720px (for 4:3 aspect ratio).
Quality level hd1080: Player height is 1080px, and player dimensions are 1920px by 1080px (for 16:9 aspect ratio) or 1440px by 1080px (for 4:3 aspect ratio).
Quality level highres: Player height is greater than 1080px, which means that the player's aspect ratio is greater than 1920px by 1080px.
My goal was to get aspect ratio for any video, not only for those for which I'm owner.
Thus the trick is to use https://developers.google.com/youtube/v3/docs/videos/list with player provided in parts and then parsing width and height of returned embed html.
Maybe not a good answer, but there seems to be an assumption amongst other answers that YouTube videos are either 16:9 or 4:3.
But they can have a pretty much arbitrary aspect ratio, and with portrait phone videos having become quite common, it's becoming less of a rarity for a video on YouTube to be something different.
For these non-standard aspect ratios, as a quick manual fudge, I've resorted to playing them in full screen, doing a screen capture, and cropping the image down.
I've put a couple of examples of arbitrary aspect videos at http://youtube-aspect-ratios.xtra.ink.