I am working on an image cropping tool at the moment, the image crop tool comes from jCrop, what I am trying to is make the image the user takes the crop from smaller than the original uploaded. Basically if uploads a landscape image I need to make the image 304px wide without alterting the aspect ratio of the shot.
For instance if the user uploads a portrait shot, I need make the image 237px without altering the aspect ratio of the shot.
Is this possible? I have access to original images sizes in my code, but I cannot work out make sure I am not altering the aspect ratio?
Yes, it's possible if the source image is already wide enough. If it's not wide enough there isn't enough data to crop and if you wanted to maintain aspect ratio, you'd need to scale the image up before the crop.
Something like this should get you started:
CropWidth=237;
AspectRatio= SourceWidth/SourceHeight;
CropHeight = CropWidth*AspectRatio;
To properly maintain aspect ratio you should use GCD:
function gcd(a, b) {
return (b == 0) ? a : gcd(b, a % b);
}
You should then do something like this:
function calcRatio(objectWidth, objectHeight) {
var ratio = gcd(objectWidth, objectHeight),
xRatio = objectWidth / ratio,
yRatio = objectHeight / ratio;
return { "x": xRatio, "y": yRatio };
}
This will get you the ratio of some object. You can use this information to figure out how large the resulting image should be.
function findSize (targetHeight, xRatio, yRatio) {
var widthCalc = Math.round((xRatio * targetHeight) / yRatio);
return { "width" : widthCalc, "height" : targetHeight };
}
var test = calcRatio(1280,720),
dimensions = findSize(400, test.x, test.y);
//400 is what we want the new height of the image to be
Those are your dimensions. If you don't have any extra "space" around your images that you need to account for then your work is done. Otherwise you need to handle a couple cases.
Related
I am trying to convert my HTML code to image using Html2Canvas Library. In order to get a high-quality image in the result, I am using scale property with value 2.
html2canvas(element, {
scale: 2
}).then(function(canvas) {
getCanvas = canvas;
var imageData = getCanvas.toDataURL("image/png", 1);
var newData = imageData.replace(/^data:image\/png/, "data:application/octet-stream");
});
Now this newData is sent to php using AJAX and to save I use below code -
define('UPLOAD_DIR', 'assets/output_images/');
$image_parts = explode(";base64,", $_POST['ImageUri']);
$image_type_aux = explode("image/", $image_parts[0]);
$image_base64 = base64_decode($image_parts[1]);
$file_name = uniqid().'.png';
$file = UPLOAD_DIR . $file_name;
$success =file_put_contents($file, $image_base64);
But it always gives me the same image as it was with the scale with value 1 and there is no improvement in the final image.
NOTE: My div dimension is 369 * 520 and generated image size is always 369*520 with scale value 1 or 2 or 3 etc.
Thanks in advance
this just can't be... if you use bitmap image multiply size won't be better quality, but lower. You'll create fake pixels as copy of originals pixels at best.
For a real scale with image(with no quality loss) you've to use scalar image such SVG who won't be in CANVAS who use bitmap image but in the DOM as regular Elements.
Obviously you can use optionals parameters with
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
where width and height will be final width and height in pixels(so no higher quality image just a larger size and a lower quality because pixels are multiplicated with fake pixels cloned form original size).
I hope i'be got your question fine.
edit: notice that to draw in canvas you've to create a image object such new Image('imagename.png'); at least but you can add the alt attributes and other stuff like an id or such. for more example source W3schools.org canvas.drawImage(...)
I have a FabricJS canvas that has to be converted to a PDF that matches the exact fractional inches for print production. Per other questions I have received, my printer is a large format printer and actually needs this/is capable to printing to the exact decimal as follows:
Width (Inches): 38.703111
Height (Inches): 25.999987
Since the requirements are a DPI of 300, I obtained the pixels by multiplying the width and height x 300 as follows:
38.703111 inches x 300 = 11610.9333 px
25.999987 inches x 300 = 7799.9961 px
With these measurements in hand, I created a FabricJS canvas that users could edit and then convert to an image (will need to figure out how to convert it to a PDF server side later/suspecting node.js with the pdfkit module).
Since it is not usable to have a 11610 x 7799px Canvas on a page, I set the size of the canvas as follows:
Width: 650px
Height: 436.6571863 (Orginal Height * New Width / Orginal Width = new height)
Here is what my Canvas looks like (HTML) and corresponding JavaScript code to render it:
<canvas id="c" width="650" height="436.6571863" style="border:1px solid"></canvas>
var canvas = new fabric.Canvas('c', {});
Users are able to edit the canvas and then convert it to an image, but this is the problem occurs. I attempt to scale the canvas via FabricJS' toDataURL method using a multiplier of 17.86297431 (Original Width in Pixels / New Width in Pixels and Original Height in Pixels / New Height in Pixels both equal a multiplier of 17.86297431) as follows:
var dataURL = canvas.toDataURL({
format: 'png',
multiplier: 17.86297431
})
document.write('<img src="' + dataURL + '"/>');
However, once it scales, the width of my outputted image appears correct at 11610, but the height is off at 7788 when it should be 7799. This output does not show fractions, but rather just the whole pixels when I inspect the element.
My question is, how can I get my FabricJS Canvas to properly scale up to the pixels (or inches) so I can have my PDF (converted from my PNG) in the correct dimensions? Is this an issue with pixel fractions not being respected/what can I do about this?
You need to set NUM_FRACTION_DIGITS in fabric.js file
By default this value = 2, you can change it and set for example = 9
Now your multiplier = 9,0123456;
But for Fabric JS your multiplier = 9,012;
If your will set NUM_FRACTION_DIGITS = 9 you will get your multiplier = 9,0123456;
Here the proof link: http://fabricjs.com/docs/fabric.Object.html
That is what you need: NUM_FRACTION_DIGITS - Defines the number of fraction digits to use when serializing object values. You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
You should not work with float dimensions, that is a bad idea. Why don't you use Math.ceil for your dimensions. This will make no visual difference and this is the approach usually used.
Also you could use the Zoomify alghoritm approach to determine your scale factor. You can start from the original dimensions and divide them by 2 until you get a value smaller than a maximum value defined by you, which you consider acceptable. That way your scale factor will be a power of 2.
I'm using a canvas to resize client-side an image before uploading it on the server.
maxWidth = 500;
maxHeight = 500;
//manage resizing
if (image.width >= image.height) {
var ratio = 1 / (image.width / maxWidth);
}
else {
var ratio = 1 / (image.height / maxHeight);
}
...
canvas.width = image.width * ratio;
canvas.height = image.height * ratio;
var resCtx = canvas.getContext('2d');
resCtx.drawImage(image, 0, 0, image.width * ratio, image.height * ratio);
This works, since on the server is saved the resized image, but there are two points i would like to improve:
image weight in KB is important to me; i want a very light weight image; even if it is resized, the canvas' image is still too heavy; i can see that the image saved on the server has a resolution of 96 DPI and 32-bit of color-depth even if the original image has a resolution of 72 DPI and 24-bit of color-depth; why? Can i set the canvas' image resolution?
the resized image does not look very nice, because the resizing proces introduce distortion... I've tried the custom algorithm by GameAlchemist in this post:
HTML5 Canvas Resize (Downscale) Image High Quality?
getting a very nice result, but then the resized image was more heavy in KB than the original one! Is there a good algorithm in order to get quality resized images keeping them lightweight?
DPI does not matter at all with images. An image at 1k x 1k will be just the same if 892478427 DPI or as 1 DPI. DPI is arbitrary in this context so ignore that part (it is only used as information for DTP programs so they know the relative size compared to document size). Images are only measured in pixels.
Canvas is RGBA based (32-bits, 24-bit colors + 8 bit alpha channel) so it's optimal form for exporting images will be in this format (ie. PNG files). You can however export 24-bits images without the alpha channel by requesting JPEG format as export format.
Compression is basically signal processing. As in all forms of (lossy) compression you try to remove frequencies in the signal in order to reduce the size. The more high frequencies there is the harder it is to compress the image (or sound or anything else signal based).
In images high frequency manifests as small details (thin lines and so forth) and as noise. To reduce the size of a compressed image you need to remove high frequencies. You can do this by using interpolation as a low-pass filter. A blur is a form of low-pass filter as well and they work in the same way in principle.
So in order to reduce image size you can apply a slight blur on the image before compressing it.
The JPEG format support quality settings which can reduce the size as well although this using a different approach than blurring:
// 0.5 = quality, lower = lower quality. Range is [0.0, 1.0]
var dataUri = canvas.toDataURL('image/jpeg', 0.5);
To blur an image you can use a simple and fast method of scaling it to half the size and then back up (with smoothing enabled). This will use interpolation as a low-pass filter by averaging the pixels.
Or you can use a "real" blur algorithm such as Gaussian blur as well as box blur, but only with a small amount.
When using the .toImage() method in KineticJS, I always seem to get a much larger image than is really necessary, with the piece I need taking up only the top left corner of the data image. My stage is scaled based on window size and a pre-defined initial size (on window resize, resize stage function called which sets the scale and the size of the container). Should I be setting some sort of crop when I use toImage() to compensate for this? Looking at the image, it seems that the overall image is about twice the size it needs to be, and the piece I need is about half the size I need, when the scale is at around 0.5 (the stage is about half size because I use Chrome and leave the developer bar open at the bottom for debugging).
Here's what I'm using now:
desc.toImage({
width: sideW / cvsObj.scale,
height: sideH / cvsObj.scale,
callback: function(img) {
desc.hide();
sideImg.transitionTo({x : sideW / 2, width : 0, duration : 0.25, callback : function() {
// add image to emulate content
var params = {name : 'descimg', width : sideW, height : sideH, image : img, x : sideW / 2, y : 0};
var image = new Kinetic.Image(params);
side.add(image);
image.setWidth(1);
sideImg.hide();
image.transitionTo({x : 0, width : sideW, duration : 0.25, callback : function() {
side.add(desc);
desc.show();
image.hide();
cvsObj.page.draw();
}});
}});
}
});
There have been improvements to KineticJs over time and functions work in a 'better' way nowadays. Try the new KineticJS 4.3:
http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.0.min.js
I posted this question two and a half months ago, and received no replies until today. I've kept up-to-date with KineticJS - even adding my own bug reports and code suggestions. However, recently I revisited this particular section of code and came up with something a bit better - the image I'm getting back is now properly cropped and can be inserted as is into my canvas layers. Here's the code:
// grp = kinetic group, iw = image width, ih = image height, rimg = returned image array for a deferred function, cvsobj.scale is a global stage scale variable (i scale the stage to fit the window)
getImage : function(grp, iw, ih, rimg) {
var dfr = $.Deferred();
var gp = grp.getAbsolutePosition();
grp.toImage({
width: iw * cvsObj.scale,
height: ih * cvsObj.scale,
x : gp.x,
y : gp.y,
callback: function(img) {
rimg.push(img);
dfr.resolve(rimg);
}
});
return dfr.promise();
}
This is an extended prototype function - part of a sub-section functionality for converting an entire layer or section of a layer into an image specifically for manipulating in an animation sequence. Hopefully this helps someone else.
Is there a way that works for all browsers?
original answer
Yes.
window.screen.availHeight
window.screen.availWidth
update 2017-11-10
From Tsunamis in the comments:
To get the native resolution of i.e. a mobile device you have to multiply with the device pixel ratio: window.screen.width * window.devicePixelRatio and window.screen.height * window.devicePixelRatio. This will also work on desktops, which will have a ratio of 1.
And from Ben in another answer:
In vanilla JavaScript, this will give you the AVAILABLE width/height:
window.screen.availHeight
window.screen.availWidth
For the absolute width/height, use:
window.screen.height
window.screen.width
var width = screen.width;
var height = screen.height;
In vanilla JavaScript, this will give you the AVAILABLE width/height:
window.screen.availHeight
window.screen.availWidth
For the absolute width/height, use:
window.screen.height
window.screen.width
Both of the above can be written without the window prefix.
Like jQuery? This works in all browsers, but each browser gives different values.
$(window).width()
$(window).height()
You can also get the WINDOW width and height, avoiding browser toolbars and... (not just screen size).
To do this, use:
window.innerWidth and window.innerHeight properties. See it at w3schools.
In most cases it will be the best way, in example, to display a perfectly centred floating modal dialog. It allows you to calculate positions on window, no matter which resolution orientation or window size is using the browser.
Do you mean display resolution (eg 72 dots per inch) or pixel dimensions (browser window is currently 1000 x 800 pixels)?
Screen resolution enables you to know how thick a 10 pixel line will be in inches. Pixel dimensions tell you what percentage of the available screen height will be taken up by a 10 pixel wide horizontal line.
There's no way to know the display resolution just from Javascript since the computer itself usually doesn't know the actual dimensions of the screen, just the number of pixels. 72 dpi is the usual guess....
Note that there's a lot of confusion about display resolution, often people use the term instead of pixel resolution, but the two are quite different. See Wikipedia
Of course, you can also measure resolution in dots per cm. There is also the obscure subject of non-square dots. But I digress.
Using jQuery you can do:
$(window).width()
$(window).height()
Trying to get this on a mobile device requires a few more steps. screen.availWidth stays the same regardless of the orientation of the device.
Here is my solution for mobile:
function getOrientation(){
return Math.abs(window.orientation) - 90 == 0 ? "landscape" : "portrait";
};
function getMobileWidth(){
return getOrientation() == "landscape" ? screen.availHeight : screen.availWidth;
};
function getMobileHeight(){
return getOrientation() == "landscape" ? screen.availWidth : screen.availHeight;
};
See Get Monitor Screen Resolution with Javascript and the window.screen object
function getScreenWidth()
{
var de = document.body.parentNode;
var db = document.body;
if(window.opera)return db.clientWidth;
if (document.compatMode=='CSS1Compat') return de.clientWidth;
else return db.clientWidth;
}
just for future reference:
function getscreenresolution()
{
window.alert("Your screen resolution is: " + screen.height + 'x' + screen.width);
}
If you want to detect screen resolution, you might want to checkout the plugin res. It allows you to do the following:
var res = require('res')
res.dppx() // 1
res.dpi() // 96
res.dpcm() // 37.79527559055118
Here are some great resolution takeaways from Ryan Van Etten, the plugin's author:
2 unit sets exist and differ at a fixed scale: device units and CSS units.
Resolution is calculated as the number of dots that can fit along a particular CSS length.
Unit conversion: 1in = 2.54cm = 96px = 72pt
CSS has relative and absolute lengths. In normal zoom: 1em = 16px
dppx is equivalent to device-pixel-ratio.
devicePixelRatio definition differs by platform.
Media queries can target min-resolution. Use with care for speed.
Here's the source code for res, as of today:
!function(root, name, make) {
if (typeof module != 'undefined' && module.exports) module.exports = make()
else root[name] = make()
}(this, 'res', function() {
var one = {dpi: 96, dpcm: 96 / 2.54}
function ie() {
return Math.sqrt(screen.deviceXDPI * screen.deviceYDPI) / one.dpi
}
function dppx() {
// devicePixelRatio: Webkit (Chrome/Android/Safari), Opera (Presto 2.8+), FF 18+
return typeof window == 'undefined' ? 0 : +window.devicePixelRatio || ie() || 0
}
function dpcm() {
return dppx() * one.dpcm
}
function dpi() {
return dppx() * one.dpi
}
return {'dppx': dppx, 'dpi': dpi, 'dpcm': dpcm}
});
if you mean browser resolution then
window.innerWidth gives you the browser resolution
you can test with http://howbigismybrowser.com/
try changing your screen resolution by zoom in / out browser and check resolution size with http://howbigismybrowser.com/
Window.innerWidth should be same as screen resolution width
Easy steps to find screen resolution is:
Copy
`My screen resolution is: ${window.screen.width} * ${window.screen.height}`
paste in browser console
hit enter