Android Cordova - How to draw photo taken on canvas? - javascript

i'm working with an hybrid Android app and I need to take a picture from camera, process the image and then show the results obtained on another window that should contain a small preview of the picture taken and some text with the results.
Here is the relevant code that is not currently working as I need:
From script of first window:
.
.
.
CameraPreview.takePicture(function(imgData){
var mImage = new Image();
mImage.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = mImage.width;
canvas.height = mImage.height;
var context = canvas.getContext('2d');
context.drawImage(mImage, 0, 0);
var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
.
.(Process imgData[])
.
localStorage.setItem("resultImage", mImage.src);
window.open('result.html','_blank','location=no');
};
mImage.src = 'data:image/jpeg;base64,'+imgData;
});
Then, "result.html" pops up, which has the following html content:
...
<body>
...
<canvas id="resultCanvas" style="position: absolute; top: 0%; left: 0%;"></canvas>
...
</body>
...
And the following javascript code:
...
var mImage = new Image();
mImage.onload = function(){
var canvas = document.getElementById('resultCanvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight*0.3; // 1/3 of phone screen size
var context = canvas.getContext('2d');
context.drawImage(mImage);
navigator.notification.alert('Height = '+mImage.height+'\n'+'Width ='+mImage.width, function(){},'Image','Done');
};
mImage.src = localStorage.getItem("resultImage");
...
The alert window shows the 640x480 of the image, but the canvas is transparent where it should contain the picture taken.
If I replace the "context.drawImage()" statement with "context.fillrect()" I can see the black rectangle drawn on screen, so it looks that the canvas is there, but somehow I can not make it draw the desired image.
Hope I explained myself correctly.
I really appreciate any help. Thanks!

Well, finally I solved it, first I tryied using an "img" element instead of a canvas, and that works fine, but the problem is I need to make some drawings over the picture, so insisting on code I posted before, I realized the problem was on the use of drawImage() method in the second script, it is necessary to specify position, so replacing:
context.drawImage(mImage);
with
context.drawImage(mImage,0,0);
solved everything.

Related

How to capture what is visible through a div?

Suppose I have an image and a div whose position is absolute and is above that image (z-index of div more than z-index of image).Something like this :
I want to take screenshot of what is visible through the div using JavaScript. At first I thought of changing the div to a canvas and then I wrote this code:
<div class="utility-btn">
<button class="enquiry-btn" onclick="openEnquiry()">?</button>
</div>
<div id="enquiry">
<button id="close" onclick="closeEnquiry()">X</button>
<div class="cover">
<canvas id="capture"></canvas>
<button class="btn" onclick="takeScreenshot()">
Click to enquiry
</button>
</div>
</div>
Function to take screenshot:
function takeScreenshot() {
var canvas = document.getElementById('capture');
ctx = canvas.getContext('2d');
var backCanvas = document.createElement('canvas');
backCanvas.width = canvas.width;
backCanvas.height = canvas.height;
var backCtx = backCanvas.getContext('2d');
backCtx.drawImage(canvas, 0, 0);
ctx.drawImage(backCanvas, 0, 0);
var dataURL = backCanvas.toDataURL();
console.log(dataURL);
}
But the image of dataURL was not what I expected it was just a blank image:
How can I implement this feature. How can I do it without using any external library?
There are two problems:
#1
If we look at your bit of code responsible for actually taking the screenshot
function takeScreenshot() {
var canvas = document.getElementById('capture');
ctx = canvas.getContext('2d');
var backCanvas = document.createElement('canvas');
backCanvas.width = canvas.width;
backCanvas.height = canvas.height;
var backCtx = backCanvas.getContext('2d');
backCtx.drawImage(canvas, 0, 0);
ctx.drawImage(backCanvas, 0, 0);
var dataURL = backCanvas.toDataURL();
console.log(dataURL);
}
we can see that canvas is the element you want to have the screenshot taken onto. Later on you're creating an empty new canvas backCanvas and make it the size of the first canvas. Afterwards you're drawing the empty first canvas on the second and finally the empty second canvas back to the first.
So that does not make too much sense.
Instead you need to take the actual canvas generated by threeJS. These lines of your code append a <canvas> element to the container <div>, threeJS is using:
const container = document.getElementById('container');
container.appendChild(renderer.domElement);
We can reference it using:
document.getElementById("container").children[0]
#2
As threeJS uses WebGL to draw stuff, it's rendering context is not the regular and for performance reasons the browser is clearing the drawing buffer after something has been drawn onto - so your screenshot would come up empty all the time. There is an option you need to pass to the THREE.WebGLRenderer constructor to keep the drawingbuffer called preserveDrawingBuffer.
So change this line:
renderer = new THREE.WebGLRenderer();
to this
renderer = new THREE.WebGLRenderer({preserveDrawingBuffer: true});
and your screenshot function to this:
function takeScreenshot() {
var canvas = document.getElementById('capture');
ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById("container").children[0], 0, 0);
var dataURL = canvas.toDataURL();
console.log(dataURL);
}

How to wait for the multiple images to load in the canvas and convert to base64?

I'm trying to get a base64 version of a canvas in HTML5.
Currently, the base64 image that I get from the canvas is blank.
I have found similar questions for example this one:
HTML Canvas image to Base64 problem
However, the issue that I have is that because I am adding 2 images in the canvas, I cannot use the example provided in those answers as most of them are using single image.
I understand that I have to wait until the canvas image is loaded properly before trying to get the base64 image. But I don't know how in my case.
This is my code:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
var imageObj1 = new Image();
imageObj1.src = "http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg";
imageObj1.onload = function() {
context.drawImage(imageObj1, 0, 180, canvas.width, canvas.height);
};
var imageObj2 = new Image();
imageObj2.src = "http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"
imageObj2.onload = function() {
context.drawImage(imageObj2, 0, 0, canvas.width, 180);
};
// get png data url
//var pngUrl = canvas.toDataURL();
var pngUrl = canvas.toDataURL('image/png');
// get jpeg data url
var jpegUrl = canvas.toDataURL('image/jpeg');
$('#base').val(pngUrl);
<div class="contents" style="overflow-x:hidden; overflow-y:scroll;">
<div style="width:100%; height:90%;">
<canvas id="myCanvas" class="snap" style="width:100%; height:100%;" onclick="takephoto()"></canvas>
</div>
</div>
<p>
This is the base64 image
</p>
<textarea id="base">
</textarea>
and this is a working FIDDLE:
https://jsfiddle.net/3p3e6Ldu/1/
Can someone please advice on this issue?
Thanks in advance.
EDIT:
As suggested in the comments bellow, i tried to use a counter and when the counter reaches a specific number, I convert the canvas to base64.
Like so:https://jsfiddle.net/3p3e6Ldu/4/
In both your examples (the one from the question and the one from the comments), the order of commands does not really respect the async nature of the task at hand.
In your later example, you should put the if( count == 2 ) block inside the onload callbacks to make it work.
However, even then you will run into the next problem: You are loading the images from different domains. You can still draw them (either into the canvas or using an <img> tag), but you are not able to access their contents directly. Not even with the detour of using the <canvas> element.
I changed to code so it would work, if the images are hosted on the same domain. I also used a function to load the image and promises to handle the callbacks. The direct way of using callbacks and a counting variable, seem error-prone to me. If you check out the respective fiddle, you will notice the SecurityError shown. This is the result of the aforementioned problem with the Same-Origin-Policy I mentioned.
A previous question of mine in a similar direction was about how to detect, if I can still read the contents of a <canvas> after adding some images.
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
// function to retrieve an image
function loadImage(url) {
return new Promise((fulfill, reject) => {
let imageObj = new Image();
imageObj.onload = () => fulfill(imageObj);
imageObj.src = url;
});
}
// get images
Promise.all([
loadImage("http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg"),
loadImage("http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"),
])
.then((images) => {
// draw images to canvas
context.drawImage(images[0], 0, 180, canvas.width, canvas.height);
context.drawImage(images[1], 0, 0, canvas.width, 180);
// export to png/jpg
const pngUrl = canvas.toDataURL('image/png');
const jpegUrl = canvas.toDataURL('image/jpeg');
// show in textarea
$('#base').val(pngUrl);
})
.catch( (e) => alert(e) );

Using `jsgif` to build layers over animated GIFs

I'm looking to get some help or guidance on the use of the excellent libgif.js library.
My objective is to take a page that has an animated gif and a png of text with transparent background, and then show the 2 images overlaid such that the resulting image can be copied to the clipboard.
I've succeeded in doing this with a static image as a template
However, if I try this with a gif, it merges the animated gif with the text image, but freezes the gif.
I've familiarized myself with the libgif.js library and have succeeded in using it to build a canvas from an animated gif and have it remain animated.
However, the text image is not being displayed in the final canvas, and I'm a little lost as to how I might go about fixing this.
Is it obvious to anyone why the textImage is being properly sized and (somewhat) apparently placed on the canvas, but not displayed in the final result?
As a side question, does anyone know why the progress bar completes quickly at first and then progresses more slowly a second time?
The HTML is rather long, but the JS from the JSFiddle is shown below (for those not willing to click through to the link).
function doit() {
var isGIF = true; // always true for now TODO: better way to test if-gif than regex for ".gif"
var previewContainer = document.getElementById("previewContainer");
var textImage = document.getElementById("textImage");
var templateImage = document.getElementById("templateImage");
var w = document.getElementById("templateImage").width;
var h = document.getElementById("templateImage").height;
previewContainer.removeChild(previewContainer.children[1]);
if (isGIF) {
var gif = new SuperGif({
gif: templateImage,
progressbar_height: 5,
auto_play: true,
loop_mode: true,
draw_while_loading: true
});
gif.load();
var canvas = gif.get_canvas();
var context = canvas.getContext('2d');
context.drawImage(textImage, 0, 0, w, h);
previewContainer.replaceChild(canvas, previewContainer.children[0]);
}
}
Note: this solution was originally based on Arend's solution in the comments of this question from this JSFiddle.
You would have to tweak the library in order to get access to their rendering loop (like a frame_rendered event).
This way, you would be able to add whatever you want over the image the library drawn at every frame.
But since I'm too lazy to dig in there, here is a workaround :
Instead of appending the canvas returned by the library in the document, you can keep it offscreen, and draw it on an other, visible, canvas.
It is on this new canvas that you will also draw your textImage, in an rAF loop.
function doit() {
var previewContainer = document.getElementById("previewContainer");
var textImage = document.getElementById("textImage");
var templateImage = document.getElementById("templateImage");
var w = templateImage.width;
var h = templateImage.height;
previewContainer.removeChild(previewContainer.children[1]);
var gif = new SuperGif({
gif: templateImage,
progressbar_height: 5,
auto_play: true,
loop_mode: true,
draw_while_loading: true
});
gif.load();
var gif_canvas = gif.get_canvas(); // the lib canvas
// a copy of this canvas which will be appended to the doc
var canvas = gif_canvas.cloneNode();
var context = canvas.getContext('2d');
function anim(t) { // our animation loop
context.clearRect(0,0,canvas.width,canvas.height); // in case of transparency ?
context.drawImage(gif_canvas, 0, 0); // draw the gif frame
context.drawImage(textImage, 0, 0, w, h); // then the text
requestAnimationFrame(anim);
};
anim();
previewContainer.replaceChild(canvas, previewContainer.children[0]);
}
<script src="https://cdn.rawgit.com/buzzfeed/libgif-js/master/libgif.js"></script>
<div>
<input type="submit" id="doit" value="Do it!" onclick="doit()" />
</div>
<div id="previewContainer">
<img id="templateImage" src="https://i.imgur.com/chWt4Yg.gif" />
<img id="textImage" src="https://i.stack.imgur.com/CmErq.png" />
</div>

Drawing an image in its full size in a canvas works only on the second try in Mozilla Firefox

I'm trying to get images from users and draw in them stuff. But I'm getting strange behaviour in MF to only show the image in its full size in a canvas.
I have the following code:
<!DOCTYPE html>
<html>
<body>
<input type="file" id="input">
<div id="main"></div>
<script>
document.getElementById('input').onchange = function() {
var file_reader = new FileReader();
file_reader.onload = function() {
var img = new Image();
img.src = file_reader.result;
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height)
document.getElementById('main').appendChild(canvas)
}
file_reader.readAsDataURL(document.getElementById('input').files[0])
}
</script>
</body>
</html>
In Chrome and Opera everything works fine, but in Mozilla Firefox when I choose an image nothing happens, the added canvas has width 0 and height 0. Then if I reselect it again it'll show up, and thus for each image, I have to select it once, nothing happens, and then on the second, third etc. times everything works fine. Why is this happening?
I'd give a try to draw the image on the canvas in the img.onload callback.

jQuery and Canvas loading behaviour

After being a long time lurker, this is my first post here! I've been RTFMing and searching everywhere for an answer to this question to no avail. I will try to be as informative as I can, hope you could help me.
This code is for my personal webpage.
I am trying to implement some sort of a modern click-map using HTML5 and jQuery.
In the website you would see the main image and a hidden canvas with the same size at the same coordinates with this picture drawn into it.
When the mouse hovers the main picture, it read the mouse pixel data (array of r,g,b,alpha) from the image drawn onto the canvas. When it sees the pixel color is black (in my case I only check the RED value, which in a black pixel would be 0) it knows the activate the relevant button.
(Originally, I got the idea from this article)
The reason I chose this method, is for the page to be responsive and dynamically change to fit different monitors and mobile devices. To achieve this, I call the DrawCanvas function every time the screen is re-sized, to redraw the canvas with the new dimensions.
Generally, this works OK. The thing is ,there seems to be an inconsistent behavior in Chrome and IE(9). When I initially open the page, I sometimes get no pixel data (0,0,0,0), until i re-size the browser. At first I figured there's some loading issues that are making this happen so I tried to hack it with setTimeout, it still doesn't work. I also tried to trigger the re-size event and call the drawCanvas function at document.ready, still didn't work.
What's bothering me is most, are the inconsistencies. Sometimes it works, sometimes is doesn't. Generally, it is more stable in chrome than in IE(9).
Here is the deprecated code:
<script type="text/javascript">
$(document).ready(function(){setTimeout(function() {
// Get main image object
var mapWrapper = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
// Create a hidden canvas the same size as the main image and append it to main div
var canvas = document.createElement('canvas');
canvas.height = mapWrapper.clientHeight;
canvas.width = mapWrapper.clientWidth;
canvas.fillStyle = 'rgb(255,255,255)';
canvas.style.display = 'none';
canvas.id = 'hiddencvs';
$('#map_wrapper').append(canvas);
// Draw the buttons image into the canvas
drawCanvas(null);
$("#map_wrapper").mousemove(function(e){
var canvas = document.getElementById('hiddencvs');
var context = canvas.getContext('2d');
var pos = findPos(this);
var x = e.pageX - pos.x;
var y = e.pageY - pos.y;
// Get pixel information array (red, green, blue, alpha)
var pixel = context.getImageData(x,y,1,1).data;
var red = pixel[0];
var main_img = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
if (red == 0)
{
...
}
else {
...
}
});
},3000);}); // End DOM Ready
function drawCanvas(e)
{
// Get context of hidden convas and set size according to main image
var cvs = document.getElementById('hiddencvs');
var ctx = cvs.getContext('2d');
var mapWrapper = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
cvs.width = mapWrapper.clientWidth;
cvs.height = mapWrapper.clientHeight;
// Create img element for buttons image
var img = document.createElement("img");
img.src = "img/main-page-buttons.png";
// Draw buttons image inside hidden canvas, strech it to canvas size
ctx.drawImage(img, 0, 0,cvs.width,cvs.height);
}
$(window).resize(function(e){
drawCanvas(e);
}
);
function findPos(obj)
{
...
}
</script>
I'd appreciate any help!
Thanks!
Ron.
You don't wait for the image to be loaded so, depending on the cache, you may draw an image or not in the canvas.
You should do this :
$(function(){
var img = document.createElement("img");
img.onload = function() {
var mapWrapper = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
...
// your whole code here !
...
}
img.src = "img/main-page-buttons.png";
});

Categories

Resources