-1

I want to read a image from my own server using Ajax and render that to Canvas.

Now I know that this can be achieved using normal image tags and canvas draw like shown below :

<img id="myImage" src="./ColorTable.PNG" style="display:none;"/>
var img = document.getElementById('myImage'); 
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);

But I don't want to use it and instead was looking if we can read the file using a Ajax call for the image src location and then render it to canvas , but for me it just shows a wrong image : Here's what I did :

var xhr = new XMLHttpRequest();
xhr.open('GET', './ColorTable.PNG', true);
xhr.responseType = 'arrayBuffer';
xhr.onload = function(e) {
    var data = this.response;
    var buf = new ArrayBuffer(data.length);
    var bufView = new Uint8Array(buf);
    for (var index = 0; index < data.length; index++) {
        bufView[index] = data.charCodeAt(index);
    }
    //initialize and get context and then render the image
    var ctx = canvas.getContext('2d');
    var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    for (var  i = 0 ; i < imgData.data.byteLength; i++) {
        imgData.data[i] = bufView[i];
        imgData.data[i++] = bufView[i];
        imgData.data[i++] = bufView[i];
        imgData.data[i++] = 255;
    }
    ctx.putImageData(imgData, 0, 0);
};
xhr.send();

But the second approach doesn't work for me as the image that is rendered is a wrong one . Can anybody help?

graphics123
  • 1,191
  • 3
  • 20
  • 57
  • 1
    May I ask you why do you want to use ajax ? – wezzy Apr 15 '16 at 14:07
  • 1
    Why don't you use an `Image` elemnt and set the `src` to whatever you want? http://stackoverflow.com/questions/6011378/how-to-add-image-to-canvas – XCS Apr 15 '16 at 14:07
  • As you can see its a Color Table so I am not recommended to read it using normal image tag. Moreover, the extension is not always .bmp or png or jpg, it can be LUT, or anything else – graphics123 Apr 15 '16 at 14:08
  • Use the img tag for all the formats that canvas supports, for the other format you'll have to write custom code to render each format depending on their file structure(or find code from someone who already did). – Musa Apr 15 '16 at 14:28

2 Answers2

1

You are trying to copy data from a un-parsed binary PNG file to the canvas which of course won't work as the PNG contains chunks and other data, and the image itself is compressed binary data which has to be deflated. Loading via AJAX won't parse the PNG, it will simply be treated as a raw binary file as any other file and the result will be random colored noise.

You have to load the PNG and use it as a source for an image element which will take care of parsing and deflating, then draw the image element to canvas (unless you want to parse the file manually, which is do-able).

You could simply load the image directly as you say, without the need for AJAX:

var img = new Image();
img.onload = function() {
    var ctx = canvas.getContext('2d');
    ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
    // next step here...
};
img.src = url;

There is no difference between these methods in terms of getting the pixel data later - in both cases CORS requirements must be fulfilled.

However, if you for some reason absolutely need AJAX (which makes no sense as you would still have to pass it through an image element): load the image data as a Blob instead of ArrayBuffer, then create an object-URL for it which can be used as image source for an image element:

var xhr = new XMLHttpRequest();
xhr.open('GET', './ColorTable.PNG', true);
xhr.responseType = 'blob';                        // we want a blob
xhr.onload = function(e) {
    var url = URL.createObjectURL(this.response); // ! URL may need prefix
    var img = new Image();
    img.onload = function() {
        URL.revokeObjectURL(this.src);            // auto-revoke can be set in some browsers
        var ctx = canvas.getContext('2d');
        ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
    };
    img.src = url;

};
xhr.send();
0

There is a problem in the code that copies bufview to the imgData.

for (var  i = 0 ; i < imgData.data.byteLength; i++) {
    imgData.data[i] = bufView[i];
    imgData.data[i++] = bufView[i];
    imgData.data[i++] = bufView[i];
    imgData.data[i++] = 255;
}

The post increment ++ operator will return i before incrementing. The first iteration of the loop will be equivalent to...

imgData.data[0] = bufView[0];
imgData.data[0] = bufView[1];
imgData.data[1] = bufView[2];
imgData.data[2] = 255;

I am assuming that you intended to use the pre increment ++ operator which will probably produce the result you want.

for (var  i = 0 ; i < imgData.data.byteLength; i++) {
    imgData.data[i] = bufView[i];
    imgData.data[++i] = bufView[i];
    imgData.data[++i] = bufView[i];
    imgData.data[++i] = 255;
}

However, it is bad practice to use pre or post increment ++ operator on a variable that is being using more that once in an expression. It is not always clear if the instance of the variable with the operator is accessed before or after the instance of the variable without the operator. In your scenario, I would suggest only incrementing the variable in the loop statement. For example...

for (var  i = 0 ; i < imgData.data.byteLength; i += 4) {
    imgData.data[i] = bufView[i];
    imgData.data[i+1] = bufView[i+1];
    imgData.data[i+2] = bufView[i+2];
    imgData.data[i+3] = 255;
}
Bobby Orndorff
  • 3,265
  • 1
  • 9
  • 7