INDEX_SIZE_ERR when drawImage on canvas - javascript

I need to draw an Image object to a canvas but I've got an INDEX_SIZE_ERR exception in Firefox and IE10 but not in Chrome nor Safari...
According to the W3C: If one of the sw or sh arguments is zero, the implementation must raise an INDEX_SIZE_ERR exception..
Here is the code that causes the problem:
function writePhotoOnCanvas(data, width, height) {
// Get the canvas
var canvasGallery = document.getElementById("canvasGallery");
// Clear the canvas
canvasGallery.width = canvasGallery.width;
// Get its context
var ctxCapture = canvasGallery.getContext("2d");
// Create an image in order to draw in the canvas
var img = new Image();
// Set image width
img.width = width;
// Set image height
img.height = height;
// To do when the image is loaded
img.onload = function() {
console.log("img.width="+img.width+", img.height="+img.height);
console.log("width="+width+", height="+height+", canvasGallery.width="+canvasGallery.width+", canvasGallery.height="+canvasGallery.height);
// Draw the picture
try {
ctxCapture.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvasGallery.width, canvasGallery.height);
} catch(e) {
console.error(e.message);
}
};
// Set image content from specified photo
img.src = data;
}
The console shows:
img.width=640, img.height=480
width=640, height=480, canvasGallery.width=589, canvasGallery.height=440
Index or size is negative or greater than the allowed amount
What is the source of the problem?
Thanks

You are manually setting the width and height of the image (img.width = width; img.height = height;). I don't really understand why you are doing this, but it is likely unnecessary.
These should be calculated automatically from the image data you load. Try to remove them to see what the actual size of the data is.

The image width and height are read-only properties so they will cause the code to break in some browser.
If you absolutely want to set the width and height before loading the image you can do:
var img = new Image(width, height);
Then you can read img.width and img.height when image has loaded (or read img.naturalWidth and img.naturalHeight to get the original dimension).
There is no need though to do this. Simply call your drawImage() like this:
ctxCapture.drawImage(img, 0, 0, canvasGallery.width, canvasGallery.height);
This will use the full dimension of the image and scale it to the canvasGallery's dimension.
Tip: If you are using this function to load several images you will want to exchange img with this inside your onload handler.
Modified code:
function writePhotoOnCanvas(data, width, height) {
var canvasGallery = document.getElementById("canvasGallery");
var ctxCapture = canvasGallery.getContext("2d");
/// setting width to clear does not work in all browser, to be sure:
ctxCapture.clearRect(0, 0, canvasGallery.width, canvasGallery.height);
var img = new Image();
img.onload = function() {
// Draw the picture
try {
ctxCapture.drawImage(this, 0, 0,
canvasGallery.width, canvasGallery.height);
} catch(e) {
console.error(e.message);
}
};
// Set image content from specified photo
img.src = data;
}

Related

JavaScript - Get width of element during css transition

I want to display an element's width in javascript. This part is already working, but here is my issue :
When the element's width is animated, during the animation I want to see the real element's width, and not the final element's width.
I'm working with Angular, and what I wanted to do was possible by including JQuery using the function $(el).width() but I want to remove JQuery uses.
I already tried (assuming el is a HTML element) :
el.offsetWidth
el.clientWidth
el.scrollWidth
el.getBoundingClientRect().width
Some time ago I faced the same issue, with images who are not loaded yet and had the size of 0. I fixed the problem with using the following code to get the original image size. Be sure you continue your code inside the onload function
var image = new Image();
image.onload = function() {
// set its dimension to target size
var width = image.width;
var height = image.height;
};
image.src = el.img;
It is also possible to do this with a base64 string if needed
var image = new Image();
image.onload = function() {
// set its dimension to target size
var width = image.width;
var height = image.height;
};
image.src = "data:image/jpg;base64," + img;

Resizing P5 Canvas according to image size

Please take a look at the following P5 code.
The point of this small app is to drag and drop an image on the canvas changing its size according to the "uploaded" image's width and height.
/**************************************************/
//GENERAL
var canvas;
//IMAGE
var img;//image
var imgW;//image width
var imgH;//image height
/**************************************************/
function setup(){
canvas = createCanvas(710, 400);//initial canvas size
canvas.drop(gotFile);
}
/**************************************************/
function draw(){
background(0);
if(img){
image(img, 0, 0);
imgW = img.width;
imgH = img.height;
if(imgW > 0 && imgH > 0){
resizeCanvas(imgW, imgH);
}
}
fill(255);
text(imgW + ", " + imgH, 10, 10);
}
/**************************************************/
//
function gotFile(file){
if (file.type === 'image') {
img = createImg(file.data).hide();
}
}
/**************************************************/
function mouseClicked(){
if(img){
if(imgW > 0 && imgH > 0){
resizeCanvas(imgW, imgH);
}
}
}
Using draw():
Once the image has been loaded successfully (at least displaying it), I assign its width and height to the variables imgW and imgH. Followed by the canvas size change... which leads to an error, the canvas' size the same as the uploaded image (checked on Developer Tools) but doesn't display anything.
Using mouseClicked():
When disabling the canvas size change on draw(), and click on the canvas, the resize happens perfectly showing the image.
The application needs to work without clicking, it should be automatic change (canvas size) after dropping the image.
UPDATE
This is what I see on the console when dropping the
The error I get on the console:
Uncaught RangeError: Maximum call stack size exceeded
at Array.map (<anonymous>)
at p5.Color._parseInputs (p5.js:44096)
at new p5.Color (p5.js:43147)
at p5.color (p5.js:42827)
at p5.Renderer2D.background (p5.js:50315)
at p5.background (p5.js:44288)
at draw (sketch.js:21)
at p5.redraw (p5.js:52435)
at p5.resizeCanvas (p5.js:51826)
at draw (sketch.js:30)
UPDATE 2
This is how the code looks like now, calling the resizeCanvas on the drop method:
/**************************************************/
//GENERAL
var canvas;
//IMAGE
var img;
var imgW;
var imgH;
var z;
/**************************************************/
function setup(){
canvas = createCanvas(710, 400);
canvas.drop(gotFile);
}
/**************************************************/
function draw(){
background(0);
//
if(img){
imgUpdate();
}
//debug
fill(255);
text(imgW + ", " + imgH, 10, 10);
}
/**************************************************/
function gotFile(file){
if (file.type === 'image') {
img = createImg(file.data).hide();
}
while (!img) {
}
createP("Uploaded");
if(img)
{
imgUpdate();
resizeCanvas(imgW, imgH);
}
}
/**************************************************/
function mouseClicked(){
if(img)
{
if(imgW > 0 && imgH > 0)
{
resizeCanvas(imgW, imgH);
}
}
}
/**************************************************/
function imgUpdate()
{
image(img, 0, 0);
imgW = img.width;
imgH = img.height;
}
/**************************************************/
The problem is the asynchronous loading of the image data. You can use the loadImage() method, take advantage of the success callback and write gotFile like this:
function gotFile(file){
if (file.type === 'image') {
img = loadImage(file.data,
function(){
imgUpdate();
resizeCanvas(imgW, imgH);
});
}
createP("Uploaded");
}
Keep in mind that the draw() function is called 60 times per second. It's also called whenever you call the resizeCanvas() function.
You call resizeCanvas() which calls draw() which calls resizeCanvas() which calls draw() until you eventually get your error.
To fix this, you need to stop calling resizeCanvas() every frame. It looks like you can probably call it from inside the gotFile() function instead?
Edit: After fixing that problem, your next problem is caused by the fact that JavaScript loads images asynchronously. Think about what happens on this line:
img = createImg(file.data).hide();
The createImg() file takes a URL, and begins loading the image in the background while your code continues to execute. Then you use img to resize the canvas, but the image isn't actually done loading yet, so the width and height are undefined. Try printing some values to the console to see what I mean.
To fix this problem, you need to wait until the image is actually loaded. I think you're trying to do that with your while loop, but note that your loop isn't doing much because img will always be defined the first time through. You need to go one level deeper and check the width and height of the image before you use it.
You could also use the p5.File reference to check the file type before continuing and do something like show a progress bar while the image loads.

image smaller than the original on canvas to make drawImage()

I am developing a system for image filter with html5 canvas, however, as I am at the beginning I have emerged me some doubts and mistakes.
Here's what I've developed so far:
$(document).ready(function() {
$("#uploadImagem").change(function(e) {
var _URL = window.URL || window.webkitURL,
arquivo = e.target.files[0],
tipoImagem = /image.*/,
reader,
imagem;
if(!arquivo.type.match(tipoImagem)) {
alert("Somente imagens são aceitas!");
return;
}
imagem = new Image();
imagem.onload = function() {
if(this.width > 600 || this.height > 400) {
alert("Somente imagens com largura máxima de 600px e altura máxima de 400px");
return;
} else {
$("#filtrarImagem").width(this.width).height(this.height);
}
};
imagem.src = _URL.createObjectURL(arquivo);
reader = new FileReader();
reader.onload = fileOnload;
reader.readAsDataURL(arquivo);
});
function fileOnload(e) {
var $img = $("<img>", {src: e.target.result}),
canvas = $("#filtrarImagem")[0],
context = canvas.getContext("2d");
$img.load(function() {
context.drawImage(this, 0, 0);
});
}
});
When I do imagem.onload... I wish, if the image pixels were greater than 600 and 400 he gave the alert and stop there, but even so the image appears on the canvas.
In the same imagem.onload... function, if the image pixels correspond to the canvas required (id = "filtrarImagem") is the size of the image, but the image to the canvas becomes smaller than the original uploaded, and she was to occupy all the canvas and get the original size.
How to continue?
You have two separate handlers for image loading. There is no need to set two image sources with the same URL, just reuse a single image for both checking size and setting canvas size as well as draw it into the canvas.
I would suggest the following steps (you tagged the question with jQuery but I would highly recommend working in vanilla JavaScript when you work with non-DOM oriented elements such as canvas).
Example
if (typeof window.URL === "undefined") window.URL = window.webkitURL;
$("input")[0].onchange = function(e) {
var arquivo = e.target.files[0],
imagem = new Image();
if (!arquivo.type.match(/image.*/)) {
alert("Somente imagens são aceitas!");
return;
}
// STEP 1: load as image
imagem.onload = doSetup;
imagem.src = URL.createObjectURL(arquivo)
};
// STEP 2: now that we have the image loaded we can check size and setup canvas
function doSetup() {
URL.revokeObjectURL(this.src); // clean up memory for object-URL
if (this.width > 600 || this.height > 400) {
alert("Too big!");
return; // exit!
}
// image OK, setup canvas
var c = $("canvas")[0]; // work with the element itself, not the jQuery object
c.width = this.width; // important: use canvas.width, not style or css
c.height = this.height;
// draw in image
var ctx = c.getContext("2d");
ctx.drawImage(this, 0, 0);
// NEXT: ... from here, invoke filter etc.
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file"><br><canvas></canvas>
The jQuery width() and height() functions use CSS to change the size of the canvas which does not do what you would expect it to do. Change the width and height attributes of the canvas element directly.
The reason why your check for the size does not prevent the image from being drawn, is because your check is in the handler for imagem.onload in $("#uploadImagem").change while the drawing happens in $img.onload assigned from reader.onload. These two events are processed independently. One of them returning prematurely does not prevent the other from being executed.

Canvas image sized wrong when using an external css file

I am working with the HTML5 canvas element. I am using the following javascript to draw an image into the canvas:
var img = new Image();
var canvas = this.e;
var ctx = canvas.getContext('2d');
img.src = options.imageSrc;
img.onload = function() {
ctx.drawImage(img,0,0);
}
This works fine. However I am experiencing some weird css issues. When I use inline css like the following, the image scales perfectly in the canvas:
<canvas id="canvas" width="800" height="448"></canvas>
However, when I remove the inline css and use an external css file the image is drawn too big for the canvas and I only see the top left corner of the image. Here is the css I am using:
#canvas {
height:448px;
width:800px;
border:none;
}
When I use the inline css is the drawn image somehow inheriting the css from the canvas? Not sure what is going on here but I would like to use the external css file.
EDIT
If I provide no styling for the canvas element, it is smaller but still shows just the top corner of the image. When I do style it with a css file, the canvas is bigger but it is as if it just zoomed in on the image. The same amount of the image is shown, it is now just bigger.
EDIT 2
Based on the answer received I am now sizing the dom size of the canvas with javascript. I changed my img.onload function to the following:
img.onload = function() {
if (options.imageWidth == 0 && options.imageHeight == 0) {
canvas.height = this.height;
canvas.width = this.width;
} else {
canvas.height = options.imageHeight;
canvas.width = options.imageWidth;
}
ctx.drawImage(img,0,0);
}
Canvases have two sizes - the actual pixel grid size (in the width and height attributes) and the CSS size.
If the pixel size is not specified it typically defaults to something like 300x300.
The CSS size only specifies the amount of space taken in the page, and defaults to the pixel size. If they are not equal the image will be scaled.
You MUST specify the pixel size in the <canvas> element, or set it using Javascript:
var img = new Image();
var canvas = this.e;
var ctx = canvas.getContext('2d');
img.src = options.imageSrc;
img.onload = function() {
canvas.width = this.width; // new code
canvas.height = this.height; // "
ctx.drawImage(img, 0, 0);
}
Try putting !important on your external styles, might be that something is overriding it, but inline it overrides last.
#canvas {
height:448px !important;
width:800px !important;
border:none !important;
}

How can I dynamically set the image size ratio depending on the returned image in raphael?

How can I dynamically set the image size ratio depending on the returned image in raphael?
Here is some code to give you an idea:
var viewer = Raphael(0,0,scrWidth, scrHeight);
viewer.image(dynamicUrl, 140, 140,300, scaledHeight);
Thank you
You can load the image outside the DOM and get its dimensions... You can put this inside a function:
var myImg = new Image();
myImg.src = dynamicUrl;
myImg.onload = function() {
var width = myImg.width;
var height = myImg.height;
var scale = 0.5; // for example
var viewer = Raphael(0,0,width, height); // or whatever other size
viewer.image(dynamicUrl, 0, 0, width*scale, height*scale); // scale image
// after the image is in the viewer you can use .scale()
}
jsFiddle
Now you can divide or multiply both width and height to scale. Make sure you pay attention to the timing.
Also, once the image is in Raphael, you can use .scale()

Categories

Resources