admin管理员组

文章数量:1026989

Given a 40x30 array of colors what would be the fastest way to display them on the screen in a 400x300 canvas? That is, each color would require 100 pixels in the canvas element.

Using the canvas element in HTML5 I created 4 possible ways to do it.

Random pixel data is loaded into the 40x30 array. It is then displayed in a canvas of size 400x300 such that each logical pixel is represented by 100 pixels on the screen.

The fastest way (using Chrome 38.0) was to create the image data in a hidden canvas and copy it using toDataURL into the canvas that is displayed on the screen. The property imageSmoothingEnabled is set to false to stop the pixels blurring when they are scaled upwards.

Is there any other method of doing this? I tried using small canvas dimensions and just scaling it up in CSS but this led to the pixels being blurred.

I'd like to know how the test stands in other web browsers; especially mobile phone ones.

Drawing the pixel data onto the screen is quite important as it needs to happen at least 30 times a second so even a small tweak could improve efficiency a lot.

Here is the code for the four ways I tested.

Method 1

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) {
var x = (i/4)%40;
    var y = Math.floor(i/160);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx1.putImageData(imgData, 0, 0);

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 2

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

var imgData = ctx.createImageData(400, 300);
for (var i=0; i<imgData.data.length; i+=4) {
    var x = Math.floor(((i/4)%400)/10);
    var y = Math.floor(i/16000);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);

Method 3

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

for (var x = 0; x < 40; x++) {
    for (var y = 0; y < 30; y++) {
        ctx1.fillStyle = pixelData2[x][y];
        ctx1.fillRect(x, y, 1, 1);
    }
}

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 4

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

for (var x=0; x<40; x++) {
    for (var y=0; y<30; y++) {
        ctx.fillStyle = pixelData2[x][y];
        ctx.fillRect(x*10, y*10, 10, 10);
    }
}

Given a 40x30 array of colors what would be the fastest way to display them on the screen in a 400x300 canvas? That is, each color would require 100 pixels in the canvas element.

Using the canvas element in HTML5 I created 4 possible ways to do it. http://jsperf./pixel-plotting

Random pixel data is loaded into the 40x30 array. It is then displayed in a canvas of size 400x300 such that each logical pixel is represented by 100 pixels on the screen.

The fastest way (using Chrome 38.0) was to create the image data in a hidden canvas and copy it using toDataURL into the canvas that is displayed on the screen. The property imageSmoothingEnabled is set to false to stop the pixels blurring when they are scaled upwards.

Is there any other method of doing this? I tried using small canvas dimensions and just scaling it up in CSS but this led to the pixels being blurred.

I'd like to know how the test stands in other web browsers; especially mobile phone ones.

Drawing the pixel data onto the screen is quite important as it needs to happen at least 30 times a second so even a small tweak could improve efficiency a lot.

Here is the code for the four ways I tested.

Method 1

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) {
var x = (i/4)%40;
    var y = Math.floor(i/160);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx1.putImageData(imgData, 0, 0);

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 2

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

var imgData = ctx.createImageData(400, 300);
for (var i=0; i<imgData.data.length; i+=4) {
    var x = Math.floor(((i/4)%400)/10);
    var y = Math.floor(i/16000);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);

Method 3

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

for (var x = 0; x < 40; x++) {
    for (var y = 0; y < 30; y++) {
        ctx1.fillStyle = pixelData2[x][y];
        ctx1.fillRect(x, y, 1, 1);
    }
}

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 4

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

for (var x=0; x<40; x++) {
    for (var y=0; y<30; y++) {
        ctx.fillStyle = pixelData2[x][y];
        ctx.fillRect(x*10, y*10, 10, 10);
    }
}
Share Improve this question edited Nov 1, 2014 at 22:59 user1544307 asked Nov 1, 2014 at 19:54 user1544307user1544307 1212 silver badges7 bronze badges 5
  • Since there is no transparency, aren't the pixels going to overwrite themselves and you'd see only the last rectangle fully? Can you provide a sample image of what you're trying to do? – Shomz Commented Nov 1, 2014 at 23:35
  • Yes they will. It's quite a generic question though. The link: jsperf./pixel-plotting shows some random pixel data (you have to click "run tests") It'll run the code numerous times to see which of the 4 methods is fastest. The resulting animation is due to the method of testing and not relevant to the question. I guess I'm asking if there's a fifth way that's faster than any of the four I've presented. – user1544307 Commented Nov 2, 2014 at 0:11
  • Yeah, yeah, saw the tests. I wanted to say that the fifth way might be only some clever optimization specific to a scenario (as in case of non-transparent pixels I mentioned where there would be no point in rendering 95% of the pixels.). Generally, I think you've covered pretty much every method possible, but we'll see if someone es up with something new. – Shomz Commented Nov 2, 2014 at 8:13
  • There is btw. no point using an image element and toDataURL when you can use the canvas as image source directly. Just draw with: ctx2.drawImage(c1, 0, 0, 400, 300); (ref. m3). – user1693593 Commented Nov 3, 2014 at 16:56
  • Wow, that made it a lot faster. It looks like the fastest way to do this is using 2 canvases and putImageData. – user1544307 Commented Nov 6, 2014 at 19:33
Add a ment  | 

1 Answer 1

Reset to default 5

Thanks for the response in the ments. From Ken Frystenberg's ment I updated the tests on jsperf: http://jsperf./pixel-plotting/5

It seems that the fastest way is to edit the image data of a canvas not visible on the screen. Then, on the second canvas's context, set the property imageSmoothingEnabled to false. Finally call drawImage passing the first canvas as the source of the image.

To support this method in most browsers the appropriate prefixes were added to the imageSmoothingEnabled property of the canvas's context.

Here is the final code:

var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) {
    var x = (i/4)%40; 
    var y = Math.floor(i/160); 
    imgData.data[i] = pixelData1[x][y].r; 
    imgData.data[i+1] = pixelData1[x][y].g; 
    imgData.data[i+2] = pixelData1[x][y].b; 
    imgData.data[i+3] = 255; 
}
ctx1.putImageData(imgData, 0, 0);

c2.width = 400;
c2.height = 300;

ctx2.mozImageSmoothingEnabled = false;
ctx2.webkitImageSmoothingEnabled = false;
ctx2.msImageSmoothingEnabled = false;
ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(c1, 0, 0, 400, 300);

Given a 40x30 array of colors what would be the fastest way to display them on the screen in a 400x300 canvas? That is, each color would require 100 pixels in the canvas element.

Using the canvas element in HTML5 I created 4 possible ways to do it.

Random pixel data is loaded into the 40x30 array. It is then displayed in a canvas of size 400x300 such that each logical pixel is represented by 100 pixels on the screen.

The fastest way (using Chrome 38.0) was to create the image data in a hidden canvas and copy it using toDataURL into the canvas that is displayed on the screen. The property imageSmoothingEnabled is set to false to stop the pixels blurring when they are scaled upwards.

Is there any other method of doing this? I tried using small canvas dimensions and just scaling it up in CSS but this led to the pixels being blurred.

I'd like to know how the test stands in other web browsers; especially mobile phone ones.

Drawing the pixel data onto the screen is quite important as it needs to happen at least 30 times a second so even a small tweak could improve efficiency a lot.

Here is the code for the four ways I tested.

Method 1

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) {
var x = (i/4)%40;
    var y = Math.floor(i/160);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx1.putImageData(imgData, 0, 0);

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 2

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

var imgData = ctx.createImageData(400, 300);
for (var i=0; i<imgData.data.length; i+=4) {
    var x = Math.floor(((i/4)%400)/10);
    var y = Math.floor(i/16000);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);

Method 3

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

for (var x = 0; x < 40; x++) {
    for (var y = 0; y < 30; y++) {
        ctx1.fillStyle = pixelData2[x][y];
        ctx1.fillRect(x, y, 1, 1);
    }
}

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 4

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

for (var x=0; x<40; x++) {
    for (var y=0; y<30; y++) {
        ctx.fillStyle = pixelData2[x][y];
        ctx.fillRect(x*10, y*10, 10, 10);
    }
}

Given a 40x30 array of colors what would be the fastest way to display them on the screen in a 400x300 canvas? That is, each color would require 100 pixels in the canvas element.

Using the canvas element in HTML5 I created 4 possible ways to do it. http://jsperf./pixel-plotting

Random pixel data is loaded into the 40x30 array. It is then displayed in a canvas of size 400x300 such that each logical pixel is represented by 100 pixels on the screen.

The fastest way (using Chrome 38.0) was to create the image data in a hidden canvas and copy it using toDataURL into the canvas that is displayed on the screen. The property imageSmoothingEnabled is set to false to stop the pixels blurring when they are scaled upwards.

Is there any other method of doing this? I tried using small canvas dimensions and just scaling it up in CSS but this led to the pixels being blurred.

I'd like to know how the test stands in other web browsers; especially mobile phone ones.

Drawing the pixel data onto the screen is quite important as it needs to happen at least 30 times a second so even a small tweak could improve efficiency a lot.

Here is the code for the four ways I tested.

Method 1

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) {
var x = (i/4)%40;
    var y = Math.floor(i/160);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx1.putImageData(imgData, 0, 0);

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 2

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

var imgData = ctx.createImageData(400, 300);
for (var i=0; i<imgData.data.length; i+=4) {
    var x = Math.floor(((i/4)%400)/10);
    var y = Math.floor(i/16000);
    imgData.data[i] = pixelData1[x][y].r;
    imgData.data[i+1] = pixelData1[x][y].g;
    imgData.data[i+2] = pixelData1[x][y].b;
    imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);

Method 3

var png = document.createElement("img");
var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

for (var x = 0; x < 40; x++) {
    for (var y = 0; y < 30; y++) {
        ctx1.fillStyle = pixelData2[x][y];
        ctx1.fillRect(x, y, 1, 1);
    }
}

png.src = c1.toDataURL("image/png");

c2.width = 400;
c2.height = 300;

ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(png, 0, 0, 400, 300);

Method 4

var c = document.getElementById("myCanvas");
c.width = 400;
c.height = 300;
var ctx = c.getContext("2d");

for (var x=0; x<40; x++) {
    for (var y=0; y<30; y++) {
        ctx.fillStyle = pixelData2[x][y];
        ctx.fillRect(x*10, y*10, 10, 10);
    }
}
Share Improve this question edited Nov 1, 2014 at 22:59 user1544307 asked Nov 1, 2014 at 19:54 user1544307user1544307 1212 silver badges7 bronze badges 5
  • Since there is no transparency, aren't the pixels going to overwrite themselves and you'd see only the last rectangle fully? Can you provide a sample image of what you're trying to do? – Shomz Commented Nov 1, 2014 at 23:35
  • Yes they will. It's quite a generic question though. The link: jsperf./pixel-plotting shows some random pixel data (you have to click "run tests") It'll run the code numerous times to see which of the 4 methods is fastest. The resulting animation is due to the method of testing and not relevant to the question. I guess I'm asking if there's a fifth way that's faster than any of the four I've presented. – user1544307 Commented Nov 2, 2014 at 0:11
  • Yeah, yeah, saw the tests. I wanted to say that the fifth way might be only some clever optimization specific to a scenario (as in case of non-transparent pixels I mentioned where there would be no point in rendering 95% of the pixels.). Generally, I think you've covered pretty much every method possible, but we'll see if someone es up with something new. – Shomz Commented Nov 2, 2014 at 8:13
  • There is btw. no point using an image element and toDataURL when you can use the canvas as image source directly. Just draw with: ctx2.drawImage(c1, 0, 0, 400, 300); (ref. m3). – user1693593 Commented Nov 3, 2014 at 16:56
  • Wow, that made it a lot faster. It looks like the fastest way to do this is using 2 canvases and putImageData. – user1544307 Commented Nov 6, 2014 at 19:33
Add a ment  | 

1 Answer 1

Reset to default 5

Thanks for the response in the ments. From Ken Frystenberg's ment I updated the tests on jsperf: http://jsperf./pixel-plotting/5

It seems that the fastest way is to edit the image data of a canvas not visible on the screen. Then, on the second canvas's context, set the property imageSmoothingEnabled to false. Finally call drawImage passing the first canvas as the source of the image.

To support this method in most browsers the appropriate prefixes were added to the imageSmoothingEnabled property of the canvas's context.

Here is the final code:

var c2 = document.getElementById("myCanvas");
var ctx2 = c2.getContext("2d");

var c1 = document.createElement("canvas");
c1.width = 40;
c1.height = 30;
var ctx1 = c1.getContext("2d");

var imgData = ctx1.createImageData(40, 30);
for (var i=0; i<imgData.data.length; i+=4) {
    var x = (i/4)%40; 
    var y = Math.floor(i/160); 
    imgData.data[i] = pixelData1[x][y].r; 
    imgData.data[i+1] = pixelData1[x][y].g; 
    imgData.data[i+2] = pixelData1[x][y].b; 
    imgData.data[i+3] = 255; 
}
ctx1.putImageData(imgData, 0, 0);

c2.width = 400;
c2.height = 300;

ctx2.mozImageSmoothingEnabled = false;
ctx2.webkitImageSmoothingEnabled = false;
ctx2.msImageSmoothingEnabled = false;
ctx2.imageSmoothingEnabled = false;
ctx2.drawImage(c1, 0, 0, 400, 300);

本文标签: javascriptHTML5 CanvasFastest way to display an array of pixel colors on the screenStack Overflow