I have this HTML code to allow a user to upload a picture on my hybrid iOS app:
<input type="file" accept="image/*">
When selected, the web view gives the user 2 options.
1. Take a new picture 2. Choose an existing picture.
When the users selects 'Choose an existing picture' everything works fine. But if the user selects 'Take a new picture', when it uploads to Firebase Storage, it somehow uploads sideways.
How can I fix this? This is not ideal, but is there a way to only allow an 'existing picture' option?
Here is my code that processes the image and shows it in a <img>.
function fileUploaded(files) {
var file = files[0];
var reader = new FileReader();
reader.onload = function (e) {
var img = document.createElement("img");
img.src = e.target.result;
img.onload = function () {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
canvas.width = 200;
canvas.height = 200;
var xStart = 0;
var yStart = 0;
var newHeight = 0;
var newWidth = 0;
var aspectRadio = img.height / img.width;
if (img.height < img.width) {
//horizontal
aspectRadio = img.width / img.height;
newHeight = 200;
newWidth = aspectRadio * 200;
xStart = -(newWidth - 200) / 2;
} else {
//vertical
newWidth = 200;
newHeight = aspectRadio * 200;
yStart = -(newHeight - 200) / 2;
}
var ctx = canvas.getContext("2d");
ctx.drawImage(img, xStart, yStart, newWidth, newHeight);
dataurl = canvas.toDataURL(file.type);
document.getElementById('img_element').src = dataurl;
}
}
reader.readAsDataURL(file);
}
Are you displaying the image before uploading? You might want to do that to see if it is already rotated by iOS (similar issue here). As for a fix, this might be what you will want to use.
Edit:
My previous response was somewhat vague. To be more precise, you're having a problem with the EXIF orientation. Here is a JSFiddle that will show you the orientation of the image and how it looks as an <img/> vs. as a <canvas>. The fix previously mentioned is still the path you will probably want to take. You should be able to use the library to correctly orient the image in a <canvas>. Here is an example of that library in use. Hope this helps!
Related
In my vb.net Project with Selenium, I have a set of JS statements to convert html to canvas to image and then download to file. The code so far is as follows:-
driver.ExecuteScript("
var url = '" + str + "';
var pages = [],
heights = [],
width = 0,
height = 0,
currentPage = 1;
var scale = 1.5;
PDFJS.disableWorker = true; // due to CORS
PDFJS.getDocument(url).then(function(pdf) {
pdf.getPage(currentPage).then(function(page) {
console.log('Printing ' + currentPage);
var viewport = page.getViewport({
scale: scale,
});
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render(renderContext).then(function() {
pages.push(ctx.getImageData(0, 0, canvas.width, canvas.height));
heights.push(height);
height += canvas.height;
if (width < canvas.width) width = canvas.width;
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
for (var i = 0; i < pages.length; i++)
ctx.putImageData(pages[i], 0, heights[i]);
document.body.appendChild(canvas);
var element = document.createElement('a');
element.setAttribute('href', document.getElementById('rakCanvas').toDataURL('image/png'));
element.setAttribute('download', '" + fileName + "');
console.log('Raky ========>' + url);
element.style.display = 'block';
document.body.appendChild(element);
element.click();
//document.body.removeChild(element);
return 'Done Rak';
});
});
});
Using jsfiddle, I found the JS code to be without any syntax errors. There are no errors during compiling and execution. And there is no file downloaded. To figure out the problem, I am looking for a way to monitor execution of each statement of above js code.
Alternatively, if there is a code chuck that could help in just converting the HTML canvas to Image to be downloaded as a file.
i am using cordova for my ios app which captures the image
the code looks
navigator.camera.getPicture(onSuccessCamera, onFailureCamera, {
quality: 25,
destinationType: navigator.camera.DestinationType.DATA_URL,
correctOrientation: true,
allowEdit:false
});
function onSuccessCamera(imageURI) {
var imgData = "data:image/jpeg;base64," + imageURI;
uploadFile(imgData);
}
function uploadFile(file){
var c=document.getElementById("picture");
c.width = window.innerWidth-50;//offset to prevent image flowing out of frame
// window.alert(window.innerWidth+":"+ window.innerHeight);414:736
c.height = "330";//window.innerHeight;//this.height;
var ctx=c.getContext("2d");
var showImg = new Image();
showImg.onload = function(){
var ratio = 1;
if (this.height > c.height) {
ratio = c.height/this.height;
}
else if (this.width>c.width) {
ratio = c.width/this.width;
}
ctx.drawImage(this,0,0, this.width*ratio, this.height*ratio);
// window.alert(c.width + ':' + c.height);
};
showImg.src = file;
}
i dont know why the image looks so horrible
It's because of the retina screen.
Make the height and width of the canvas to be the double, but then style it to be half pixels
c.width = (window.innerWidth-50)*2;//offset to prevent image flowing out of frame
c.height = 330*2;//window.innerHeight;//this.height;
c.style.width = (c.width/2)+"px";
c.style.height = (c.height/2)+"px";
Also, you can consider using a higher value on quality camera option.
I have a website which targets mobile phones, if a user clicks on a 16:9 scaled responsive image, I want clicked image to be displayed landscape, so that the user can rotate his phone to view the image in full size. I can't save the image rotated because it is an external resource. Is there any way I can achieve the desired effect?
I can't simply rotate 90° because that would rotate the scaled image.
If I rotate the unscaled image, it extends the portrait viewport to landscape width and displays the rotated image centered.
I already tried using a canvas, but it didn't nearly fit my needs, the image gets destroyed by bad downscaling on Firefox using JSFiddle while it didn't display at all on the Intel XDK emulator.
Since my scenario doesn't seem that uncommon to me, I'm wondering whether I missed an easy solution.
Found a jsfiddle doing that, and made it simpler for you.
Check it out: http://jsfiddle.net/7fw66/39/
html:
<div>
<canvas id="canvas"><img id="image" src="http://3.bp.blogspot.com/-JhISDA9aj1Q/UTECr1GzirI/AAAAAAAAC2o/5qmvWZiCMRQ/s1600/Twitter.png" /></canvas>
</div>
javascript:
var screenwidth = 400;
var imgurl = "http://3.bp.blogspot.com/-JhISDA9aj1Q/UTECr1GzirI/AAAAAAAAC2o/5qmvWZiCMRQ/s1600/Twitter.png";
$(document).ready(function () {
loadImage();
$('#restore').click(function () {
loadImage();
});
$('#rotate_ccw').click(function () {
rotate_ccw();
});
$('#rotate_cw').click(function () {
rotate_cw();
});
});
function loadImage() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, 1, 0, 0);
var img = new Image();
if (imgurl == null || imgurl == "") {
imgurl = defaultimgurl;
}
img.src = imgurl;
var maxsize = screenwidth;
var w = maxsize;
var ratio = (img.width / w);
var h = (img.height / ratio);
canvas.width = w;
canvas.height = h;
img.onload = function () {
ctx.drawImage(img, 0, 0, w, h);
};
ctx.save();
}
function rotate_cw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imgurl;
var maxsize = screenwidth;
var w = maxsize;
var ratio = (img.width / w);
var h = (img.height / ratio);
canvas.width = h;
canvas.height = w;
ctx.translate(w - h, w);
ctx.rotate((-90 * Math.PI) / 180);
ctx.translate(0, -(w - h));
ctx.drawImage(img, 0, 0, w, h);
ctx.save();
}
function rotate_ccw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imgurl;
var maxsize = screenwidth;
var w = maxsize;
var ratio = (img.width / w);
var h = (img.height / ratio);
canvas.width = h;
canvas.height = w;
ctx.translate(h, 0);
ctx.rotate((90 * Math.PI) / 180);
ctx.drawImage(img, 0, 0, w, h);
ctx.save();
}
function urlProvided() {
var userinput = document.getElementById('imageurl');
imgurl = userinput.value;
loadImage();
}
I'm trying to place a background image on the back of this canvas script I found. I know it's something to do with the context.fillstyle but not sure how to go about it. I'd like that line to read something like this:
context.fillStyle = "url('http://www.samskirrow.com/background.png')";
Here is my current code:
var waveform = (function() {
var req = new XMLHttpRequest();
req.open("GET", "js/jquery-1.6.4.min.js", false);
req.send();
eval(req.responseText);
req.open("GET", "js/soundmanager2.js", false);
req.send();
eval(req.responseText);
req.open("GET", "js/soundcloudplayer.js", false);
req.send();
eval(req.responseText);
req.open("GET", "js/raf.js", false);
req.send();
eval(req.responseText);
// soundcloud player setup
soundManager.usePolicyFile = true;
soundManager.url = 'http://www.samskirrow.com/client-kyra/js/';
soundManager.flashVersion = 9;
soundManager.useFlashBlock = false;
soundManager.debugFlash = false;
soundManager.debugMode = false;
soundManager.useHighPerformance = true;
soundManager.wmode = 'transparent';
soundManager.useFastPolling = true;
soundManager.usePeakData = true;
soundManager.useWaveformData = true;
soundManager.useEqData = true;
var clientID = "345ae40b30261fe4d9e6719f6e838dac";
var playlistUrl = "https://soundcloud.com/kyraofficial/sets/kyra-ft-cashtastic-good-love";
var waveLeft = [];
var waveRight = [];
// canvas animation setup
var canvas;
var context;
function init(c) {
canvas = document.getElementById(c);
context = canvas.getContext("2d");
soundManager.onready(function() {
initSound(clientID, playlistUrl);
});
aniloop();
}
function aniloop() {
requestAnimFrame(aniloop);
drawWave();
}
function drawWave() {
var step = 10;
var scale = 60;
// clear
context.fillStyle = "#ff19a7";
context.fillRect(0, 0, canvas.width, canvas.height);
// left wave
context.beginPath();
for ( var i = 0; i < 256; i++) {
var l = (i/(256-step)) * 1000;
var t = (scale + waveLeft[i] * -scale);
if (i == 0) {
context.moveTo(l,t);
} else {
context.lineTo(l,t); //change '128' to vary height of wave, change '256' to move wave up or down.
}
}
context.stroke();
// right wave
context.beginPath();
context.moveTo(0, 256);
for ( var i = 0; i < 256; i++) {
context.lineTo(4 * i, 255 + waveRight[i] * 128.);
}
context.lineWidth = 0.5;
context.strokeStyle = "#000";
context.stroke();
}
function updateWave(sound) {
waveLeft = sound.waveformData.left;
}
return {
init : init
};
})();
Revised code - currently just showing black as the background, not an image:
// canvas animation setup
var backgroundImage = new Image();
backgroundImage.src = 'http://www.samskirrow.com/images/main-bg.jpg';
var canvas;
var context;
function init(c) {
canvas = document.getElementById(c);
context = canvas.getContext("2d");
soundManager.onready(function() {
initSound(clientID, playlistUrl);
});
aniloop();
}
function aniloop() {
requestAnimFrame(aniloop);
drawWave();
}
function drawWave() {
var step = 10;
var scale = 60;
// clear
context.drawImage(backgroundImage, 0, 0);
context.fillRect(0, 0, canvas.width, canvas.height);
// left wave
context.beginPath();
for ( var i = 0; i < 256; i++) {
var l = (i/(256-step)) * 1000;
var t = (scale + waveLeft[i] * -scale);
if (i == 0) {
context.moveTo(l,t);
} else {
context.lineTo(l,t); //change '128' to vary height of wave, change '256' to move wave up or down.
}
}
context.stroke();
// right wave
context.beginPath();
context.moveTo(0, 256);
for ( var i = 0; i < 256; i++) {
context.lineTo(4 * i, 255 + waveRight[i] * 128.);
}
context.lineWidth = 0.5;
context.strokeStyle = "#ff19a7";
context.stroke();
}
function updateWave(sound) {
waveLeft = sound.waveformData.left;
}
return {
init : init
};
})();
Theres a few ways you can do this. You can either add a background to the canvas you are currently working on, which if the canvas isn't going to be redrawn every loop is fine. Otherwise you can make a second canvas underneath your main canvas and draw the background to it. The final way is to just use a standard <img> element placed under the canvas. To draw a background onto the canvas element you can do something like the following:
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 903;
canvas.height = 657;
var background = new Image();
background.src = "http://www.samskirrow.com/background.png";
// Make sure the image is loaded first otherwise nothing will draw.
background.onload = function(){
ctx.drawImage(background,0,0);
}
// Draw whatever else over top of it on the canvas.
Why don't you style it out:
<canvas id="canvas" width="800" height="600" style="background: url('./images/image.jpg')">
Your browser does not support the canvas element.
</canvas>
Make sure that in case your image is not in the dom, and you get it from local directory or server, you should wait for the image to load and just after that to draw it on the canvas.
something like that:
function drawBgImg() {
let bgImg = new Image();
bgImg.src = '/images/1.jpg';
bgImg.onload = () => {
gCtx.drawImage(bgImg, 0, 0, gElCanvas.width, gElCanvas.height);
}
}
Canvas does not using .png file as background image. changing to other file extensions like gif or jpg works fine.
I have some JS that makes some manipulations with images. I want to have pixelart-like graphics, so I had to enlarge original images in graphics editor.
But I think it'd be good idea to make all the manipulations with the small image and then enlarge it with html5 functionality. This will save bunch of processing time (because now my demo (warning: domain-name may cause some issues at work etc) loads extremely long in Firefox, for example).
But when I try to resize the image, it gets resampled bicubically. How to make it resize image without resampling? Is there any crossbrowser solution?
image-rendering: -webkit-optimize-contrast; /* webkit */
image-rendering: -moz-crisp-edges /* Firefox */
http://phrogz.net/tmp/canvas_image_zoom.html can provide a fallback case using canvas and getImageData. In short:
// Create an offscreen canvas, draw an image to it, and fetch the pixels
var offtx = document.createElement('canvas').getContext('2d');
offtx.drawImage(img1,0,0);
var imgData = offtx.getImageData(0,0,img1.width,img1.height).data;
// Draw the zoomed-up pixels to a different canvas context
for (var x=0;x<img1.width;++x){
for (var y=0;y<img1.height;++y){
// Find the starting index in the one-dimensional image data
var i = (y*img1.width + x)*4;
var r = imgData[i ];
var g = imgData[i+1];
var b = imgData[i+2];
var a = imgData[i+3];
ctx2.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
ctx2.fillRect(x*zoom,y*zoom,zoom,zoom);
}
}
More: MDN docs on image-rendering
I wrote a NN resizing script a while ago using ImageData (around line 1794)
https://github.com/arahaya/ImageFilters.js/blob/master/imagefilters.js
You can see a demo here
http://www.arahaya.com/imagefilters/
unfortunately the builtin resizing should be slightly faster.
This CSS on the canvas element works:
image-rendering: pixelated;
This works in Chrome 93, as of September 2021.
You can simply set context.imageSmoothingEnabled to false. This will make everything drawn with context.drawImage() resize using nearest neighbor.
// the canvas to resize
const canvas = document.createElement("canvas");
// the canvas to output to
const canvas2 = document.createElement("canvas");
const context2 = canvas2.getContext("2d");
// disable image smoothing
context2.imageSmoothingEnabled = false;
// draw image from the canvas
context2.drawImage(canvas, 0, 0, canvas2.width, canvas2.height);
This has better support than using image-rendering: pixelated.
I'll echo what others have said and tell you it's not a built-in function. After running into the same issue, I've made one below.
It uses fillRect() instead of looping through each pixel and painting it. Everything is commented to help you better understand how it works.
//img is the original image, scale is a multiplier. It returns the resized image.
function Resize_Nearest_Neighbour( img, scale ){
//make shortcuts for image width and height
var w = img.width;
var h = img.height;
//---------------------------------------------------------------
//draw the original image to a new canvas
//---------------------------------------------------------------
//set up the canvas
var c = document.createElement("CANVAS");
var ctx = c.getContext("2d");
//disable antialiasing on the canvas
ctx.imageSmoothingEnabled = false;
//size the canvas to match the input image
c.width = w;
c.height = h;
//draw the input image
ctx.drawImage( img, 0, 0 );
//get the input image as image data
var inputImg = ctx.getImageData(0,0,w,h);
//get the data array from the canvas image data
var data = inputImg.data;
//---------------------------------------------------------------
//resize the canvas to our bigger output image
//---------------------------------------------------------------
c.width = w * scale;
c.height = h * scale;
//---------------------------------------------------------------
//loop through all the data, painting each pixel larger
//---------------------------------------------------------------
for ( var i = 0; i < data.length; i+=4 ){
//find the colour of this particular pixel
var colour = "#";
//---------------------------------------------------------------
//convert the RGB numbers into a hex string. i.e. [255, 10, 100]
//into "FF0A64"
//---------------------------------------------------------------
function _Dex_To_Hex( number ){
var out = number.toString(16);
if ( out.length < 2 ){
out = "0" + out;
}
return out;
}
for ( var colourIndex = 0; colourIndex < 3; colourIndex++ ){
colour += _Dex_To_Hex( data[ i+colourIndex ] );
}
//set the fill colour
ctx.fillStyle = colour;
//---------------------------------------------------------------
//convert the index in the data array to x and y coordinates
//---------------------------------------------------------------
var index = i/4;
var x = index % w;
//~~ is a faster way to do 'Math.floor'
var y = ~~( index / w );
//---------------------------------------------------------------
//draw an enlarged rectangle on the enlarged canvas
//---------------------------------------------------------------
ctx.fillRect( x*scale, y*scale, scale, scale );
}
//get the output image from the canvas
var output = c.toDataURL("image/png");
//returns image data that can be plugged into an img tag's src
return output;
}
Below is an example of it in use.
Your image would appear in the HTML like this:
<img id="pixel-image" src="" data-src="pixel-image.png"/>
The data-src tag contains the URL for the image you want to enlarge. This is a custom data tag. The code below will take the image URL from the data tag and put it through the resizing function, returning a larger image (30x the original size) which then gets injected into the src attribute of the img tag.
Remember to put the function Resize_Nearest_Neighbour (above) into the <script> tag before you include the following.
function Load_Image( element ){
var source = element.getAttribute("data-src");
var img = new Image();
img.addEventListener("load", function(){
var bigImage = Resize_Nearest_Neighbour( this, 30 );
element.src = bigImage;
});
img.src = source;
}
Load_Image( document.getElementById("pixel-image") );
There is no built-in way. You have to do it yourself with getImageData.
Based on Paul Irish's comment:
function resizeBase64(base64, zoom) {
return new Promise(function(resolve, reject) {
var img = document.createElement("img");
// once image loaded, resize it
img.onload = function() {
// get image size
var imageWidth = img.width;
var imageHeight = img.height;
// create and draw image to our first offscreen canvas
var canvas1 = document.createElement("canvas");
canvas1.width = imageWidth;
canvas1.height = imageHeight;
var ctx1 = canvas1.getContext("2d");
ctx1.drawImage(this, 0, 0, imageWidth, imageHeight);
// get pixel data from first canvas
var imgData = ctx1.getImageData(0, 0, imageWidth, imageHeight).data;
// create second offscreen canvas at the zoomed size
var canvas2 = document.createElement("canvas");
canvas2.width = imageWidth * zoom;
canvas2.height = imageHeight * zoom;
var ctx2 = canvas2.getContext("2d");
// draw the zoomed-up pixels to a the second canvas
for (var x = 0; x < imageWidth; ++x) {
for (var y = 0; y < imageHeight; ++y) {
// find the starting index in the one-dimensional image data
var i = (y * imageWidth + x) * 4;
var r = imgData[i];
var g = imgData[i + 1];
var b = imgData[i + 2];
var a = imgData[i + 3];
ctx2.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")";
ctx2.fillRect(x * zoom, y * zoom, zoom, zoom);
}
}
// resolve promise with the zoomed base64 image data
var dataURI = canvas2.toDataURL();
resolve(dataURI);
};
img.onerror = function(error) {
reject(error);
};
// set the img soruce
img.src = base64;
});
}
resizeBase64(src, 4).then(function(zoomedSrc) {
console.log(zoomedSrc);
});
https://jsfiddle.net/djhyquon/69/