Capturing only a portion of canvas with .todataurl Javascript/HTML5 - javascript

I can capture a full canvas with .todataurl without a problem. But I do not see or know if there is anyway to capture only a portion of the canvas and save that to image.
e.i. Mr. Potatohead script draws hats, hands feet faces etc etc. mixed all over the canvas and you can drag and drop them onto the mr potato in the center of the canvas. Press a button save the image of mr potato looking all spiffy to jpg for you. Without all the extra hats/feet/faces in the image.
I have resigned myself to the fact that this is impossible based on everything I've read. But you folks have proven to be smarter than google (or atleast google in my hands) a few times so i am taking a shot.
Sorry no code to post this time... unless you want this:
var canvas = document.getElementById("mrp");
var dataUrl = canvas.toDataURL();
window.open(dataUrl, "toDataURL() image", "width=800, height=600");
But that is just the example of dataurl i am working off of.. and it works outside of the fact it doesnt cap just the mr potato
My fallback is to pass the image to php and work with it there to cut out everything i dont want then pass it back.
EDIT
tmcw had a method for doing this. Not sure if its the way it SHOULD be done but it certainly works.
document.getElementById('grow').innerHTML="<canvas id='dtemp' ></canvas>";
var SecondaryCanvas = document.getElementById("dtemp");
var SecondaryCanvas_Context = SecondaryCanvas.getContext ("2d");
SecondaryCanvas_Context.canvas.width = 600;
SecondaryCanvas_Context.canvas.height = 600;
var img = new Image();
img.src = MainCanvas.toDataURL('image/png');
SecondaryCanvas_Context.drawImage(img, -400, -300);
var du = SecondaryCanvas.toDataURL();
window.open(du, "toDataURL() image", "width=600, height=600");
document.getElementById('grow').innerHTML="";
grow is an empty span tag, SecondaryCanvas is a var created just for this
SecondaryCanvas_Context is the getcontext of SecondaryCanvas
img created just to store the .toDataURL() of the main canvas containing the Mr. PotatoHead
drawImage with negative (-) offsets to move image of MainCanvas so that just the portion i want is showing.
Then cap the new canvas that was just created and open a new window with the .png
on and if you get an error from the script saying security err 18 its because you forgot to rename imgTop to img with the rest of the variables you copy pasted and chrome doesnt like it when you try to save local content images like that.

Here's a method that uses an off-screen canvas:
var canvas = document.createElement('canvas');
canvas.width = desiredWidth;
canvas.height = desiredHeight;
canvas.getContext('2d').drawImage(originalCanvas,x,y,w,h,0,0,desiredWidth, desiredHeight);
result = canvas.toDataURL()

Create a new Canvas object of a specific size, use drawImage to copy a specific part of your canvas to a specific area of the new one, and use toDataURL() on the new canvas.

A bit more efficient (and maybe a cleaner) way of extracting part of the image:
// x,y are position in the original canvas you want to take part of the image
// desiredWidth,desiredHeight is the size of the image you want to have
// get raw image data
var imageContentRaw = originalCanvas.getContext('2d').getImageData(x,y,desiredWidth,desiredHeight);
// create new canvas
var canvas = document.createElement('canvas');
// with the correct size
canvas.width = desiredWidth;
canvas.height = desiredHeight;
// put there raw image data
// expected to be faster as tere are no scaling, etc
canvas.getContext('2d').putImageData(imageContentRaw, 0, 0);
// get image data (encoded as bas64)
result = canvas.toDataURL("image/jpeg", 1.0)

you can give left,top,width and Height parameters to toDataURL function.. Here is the code to get data image depending on the object on canvas.
mainObj = "your desired Object"; // for example canvas._objects[0];
var image = canvas.toDataURL({ left: mainObj.left, top:mainObj.top,
width: mainObj.width*mainObj.scaleX, height: mainObj.height*mainObj.scaleY});

Related

Converting simple html to png image via SVG images and converting to base64 encoding

I am trying to convert some simple html to a png image to be printed on a pdf. I do so by creating an svg with a foreign object that contains my page's html. My issue is that I am not seeing consistent results between a simple fiddle I made and my actual application despite copy/pasting the same exact code in both places.
I have my system application and I have my fiddle both running the same exact function, and inside that function I basically set a tempImg.src to that of my svg and then I can draw that image into a canvas and finally grab the canvas.toDataURL() which in theory would give a consistent base64 result that can be used.
getChartAndDetailsImageData: function (){
var me = this,
tempImg = new Image(900,450);
var imageLoaded = function (event){
var canvas = document.createElement('canvas'),
img = event.currentTarget;
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL('image/png');
console.log(dataURL);
};
var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="900" height="450"><foreignObject width="100%" height="100%"><body xmlns="http://www.w3.org/1999/xhtml"><div><b>Ya YEET!</b><ol><li>Hello World :)</li></ol></div></body></foreignObject></svg>';
var imgSrc = 'data:image/svg+xml,' + encodeURIComponent(svg);
console.log(imgSrc);
tempImg.onload = imageLoaded;
tempImg.src = imgSrc;
}
Now, from running this code I get two different results based on where I am running.
1: The fiddle: my end result image is -
Which is the desired result, This is what I expect and need to see
But my system application gives me this resulting image -
Which is really shrunk down and looks horrible.
Is there some detail I'm missing about svg's and how to acquire a proper consistent base64 encoding of the png image?
Eg.) Plug both those encodings into this website to visually see the difference (https://codebeautify.org/base64-to-image-converter)
Any help and/or explanation would be greatly appreciated.
Edit.) It appears as though I may have figured it out. I just wrapped my inner html with a div and slapped a fontsize on it and now I seem to be getting consistent results. I'm not entirely sure this was the most effective way to achieve the desired results so any sort of explanation and/or other possible fixes would be greatly appreciated.
Thanks, Y'all!

display .raw file image in browser

I have a image file in .raw format which is directly read from fingerprint scanner device. We have to display that in a browser using html and javascript. How can we convert the .raw image and display in the browser?
Following is the manual steps I used to convert using online tools
I am able to convert that hex content as .raw file using online converter http://tomeko.net/online_tools/hex_to_file.php?lang=en
and converted raw file can be converted again as jpeg file by https://www.iloveimg.com/convert-to-jpg/raw-to-jpg url
Sample file will look like this https://imgur.com/a/4snUAFL
I tried the following code to display hex content in the browser but didnt work.
function hexToBase64(str) {
return btoa(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
var img = new Image();
img.src = "data:image/jpeg;base64,"+hexToBase64(getBinary());
document.body.appendChild(img);
complete jsfiddle is http://jsfiddle.net/varghees/79NnG/1334/
First, what you have provided in your fiddle is probably not a .raw file.
While there are tons of different file formats using this extension, I don't quite bite the fact there is no metadata at all, since this is required to at least know the image's size.
So I'm sorry for future readers, but this answer only shows how to convert raw 8bit values into an actual image...
So now, without image size, but if the image is squared, we can actually do it from the byteLength only, (both width and height will be the square-root of the byteLength).
The general steps are
(convert your hex string to an actual Uint8Array)
set all 4th values of an Uint8ClampedArray 4 times bigger than the first Uint8Array (this will set the Alpha channel of our soon to be RGBA image)
pass this Uint8ClampedArray in the ImageData() constructor.
put this ImageData on a canvas
Tadaa!
So using a square full of random values (and thus avoid the hex to buffer conversion):
const fake = new Uint8Array( 256*256 );
crypto.getRandomValues(fake); // get random values
processSquareBitmap(fake.buffer);
function processSquareBitmap(buffer) {
const view = new Uint8Array(buffer);
const out = new Uint8ClampedArray(buffer.byteLength * 4);
const size = Math.sqrt(view.length);
if(size % 1) {
console.error('not a square');
return;
}
// set alpha channel
view.forEach((a,i)=>out[(i*4)+3] = a);
const image = new ImageData(out, size, size)
const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;
canvas.getContext('2d').putImageData(image, 0,0);
// if you want to save a png version
// canvas.toBlob(b=> saveAs(b, 'bitmap.png'));
document.body.appendChild(canvas);
}
But for not squared images, you must have the actual width and height.
I was able to deduce the ones of OP's hex data, and thus could make this fiddle which will display their image.

Test canvas drawings with Protractor

Is there a way to test if a drawing was made on a canvas using Protractor?
i.e. I draw a rectangle based on user clicks:
var shape = new createjs.Shape();
shape.graphics.beginStroke("black");
shape.graphics.drawRect(crd.x, crd.y, crd.width, crd.height);
stage.addChild(shape)
stage.update()
Now I want to make a spec to test if a rectangle was drawn on the specified coordinates and, as a plus, to test if its borders are of color black.
Is this possible using Protractor/WebDriverJS API?
The way that we test our canvas in protractor is as follows:
We set up a "well known" base64 image string that represents what we want our canvas to be after we draw on it. Then we use browser.executeScript to get the dataUrl of the canvas. We then compare string to string and that tells us if the drawing was correct or not.
var base64ImageString = "data:image/png;base64,iVBORw0KGgoAA...snipped for brevity";
describe("The Canvas", function () {
browser.get('/#'));
/*
.
do your drawing
.
*/
it("should contain the right drawings", function(){
browser.executeScript("return document.getElementsByTagName('canvas')[0].toDataURL()").then(function (result) {
expect(result).toBe(base64ImageString);
});
});
});
Works like a champ for us. We're experimenting with getting the Uint8ClampedArray to see if it's any easier - but so far this method is great except for a subtle gotcha.
In our experience, the image string that we get back from the toDataUrl method only represents the visible area of the canvas - not the entire canvas. It's good enough for us - but your mileage may vary. It's also why we're experimenting with thy byte array because it allows you to specify a specific X x Y area of the canvas.
This might be possible but you would have to create a dummy canvas with the desired output.
you could compare the Imagedata from the dummyCanvas to the imagedata from the browser objects canvas.
It should look something along the lines of:
describe('Canvas Test', function() {
it('should have a title', function() {
browser.get('http://whenthetestShouldrun.com');
var dummyCanvas= document.createElement('canvas');
//some code to edit the canvas to what you want
expect(browser.By.id('canvas').getImageData(imageX, imageY, imageWidth, imageHeight)).toEqual(dummyCanvas.getImageData(imageX, imageY, imageWidth, imageHeight));
});
});

HTML Canvas imageData array all 0's

var img, imageData,width,height;
var c = canvasEle.getContext("2d");
width = canvasEle.width;
height = canvasEle.height;
img = document.getElementById("id");
c.drawImage(img,0,0);
imageData = c.createImageData(width, height);
After I draw the image onto the context, then create an imageData array, the values of the array are all 0.
I have been struggling with this for hours and couldn't find any solution. The image is shown on the canvas after I draw it, but the imageData of the context says all the pixels are white. This doesn't make any sense to me.
With createImageData you are creating new image data for an empty image. Please use getImageData to get the image data from an already existing canvas

Mask for putImageData with HTML5 canvas?

I want to take an irregularly shaped section from an existing image and render it as a new image in Javascript using HTML5 canvases. So, only the data inside the polygon boundary will be copied. The approach I came up with involved:
Draw the polygon in a new canvas.
Create a mask using clip
Copy the data from the original canvas using getImageData (a rectangle)
Apply the data to the new canvas using putImageData
It didn't work, the entire rectangle (e.g. the stuff from the source outside the boundary) is still appearing. This question explains why:
"The spec says that putImageData will not be affected by clipping regions." Dang!
I also tried drawing the shape, setting context.globalCompositeOperation = "source-in", and then using putImageData. Same result: no mask applied. I suspect for a similar reason.
Any suggestions on how to accomplish this goal? Here's basic code for my work in progress, in case it's not clear what I'm trying to do. (Don't try too hard to debug this, it's cleaned up/extracted from code that uses a lot of functions that aren't here, just trying to show the logic).
// coords is the polygon data for the area I want
context = $('canvas')[0].getContext("2d");
context.save();
context.beginPath();
context.moveTo(coords[0], coords[1]);
for (i = 2; i < coords.length; i += 2) {
context.lineTo(coords[i], coords[i + 1]);
}
//context.closePath();
context.clip();
$img = $('#main_image');
copy_canvas = new_canvas($img); // just creates a new canvas matching dimensions of image
copy_ctx = copy.getContext("2d");
tempImage = new Image();
tempImage.src = $img.attr("src");
copy_ctx.drawImage(tempImage,0,0,tempImage.width,tempImage.height);
// returns array x,y,x,y with t/l and b/r corners for a polygon
corners = get_corners(coords)
var data = copy_ctx.getImageData(corners[0],corners[1],corners[2],corners[3]);
//context.globalCompositeOperation = "source-in";
context.putImageData(data,0,0);
context.restore();
dont use putImageData,
just make an extra in memory canvas with document.createElement to create the mask and apply that with a drawImage() and the globalCompositeOperation function (depending on the order you need to pick the right mode;
I do something similar here the code is here (mind the CasparKleijne.Canvas.GFX.Composite function)

Categories

Resources