在HTML5 canvas中设置单个像素的最佳方法是什么?

时间:2021-06-21 03:15:05

The HTML5 Canvas has no method for explicitly setting a single pixel.

HTML5 Canvas没有明确设置单个像素的方法。

It might be possible to set a pixel using a very short line, but then antialising and line caps might interfere.

可以使用非常短的线来设置一个像素,但是随后的反效果和线帽可能会干扰。

Another way might be to create a small ImageData object and using:

另一种方法可能是创建一个小型ImageData对象并使用:

context.putImageData(data, x, y)

to put it in place.

把它放到位。

Can anyone describe an efficient and reliable way of doing this?

有没有人能描述一种有效而可靠的方法?

12 个解决方案

#1


234  

There are two best contenders:

最好的竞争者有两个:

  1. Create a 1×1 image data, set the color, and putImageData at the location:

    创建一个1×1图像数据,设置颜色,和putImageData位置:

    var id = myContext.createImageData(1,1); // only do this once per page
    var d  = id.data;                        // only do this once per page
    d[0]   = r;
    d[1]   = g;
    d[2]   = b;
    d[3]   = a;
    myContext.putImageData( id, x, y );     
    
  2. Use fillRect() to draw a pixel (there should be no aliasing issues):

    使用fillRect()绘制像素(不存在混叠问题):

    ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
    ctx.fillRect( x, y, 1, 1 );
    

You can test the speed of these here: http://jsperf.com/setting-canvas-pixel/9 or here https://www.measurethat.net/Benchmarks/Show/1664/1

您可以在这里测试它们的速度:http://jsperf.com/setting-canvas-pixel/9,或者https://www.measurethat.net/Benchmarks/Show/1664/1

I recommend testing against browsers you care about for maximum speed. As of July 2017, fillRect() is 5-6× faster on Firefox v54 and Chrome v59 (Win7x64).

我建议针对您所关心的浏览器进行测试,以获得最高的速度。截至2017年7月,fillRect()5 - 6×速度v54 Firefox和Chrome v59(Win7x64)。

Other, sillier alternatives are:

其他愚蠢的选择是:

  • using getImageData()/putImageData() on the entire canvas; this is about 100× slower than other options.

    在整个画布上使用getImageData()/putImageData();这是关于100×低于其他选项。

  • creating a custom image using a data url and using drawImage() to show it:

    使用数据url创建自定义映像并使用drawImage()来显示:

    var img = new Image;
    img.src = "data:image/png;base64," + myPNGEncoder(r,g,b,a);
    // Writing the PNGEncoder is left as an exercise for the reader
    
  • creating another img or canvas filled with all the pixels you want and use drawImage() to blit just the pixel you want across. This would probably be very fast, but has the limitation that you need to pre-calculate the pixels you need.

    创建另一个img或canvas,填充所有你想要的像素,并使用drawImage()来设置你想要的像素。这可能非常快,但是有一个限制,就是您需要预先计算您需要的像素。

Note that my tests do not attempt to save and restore the canvas context fillStyle; this would slow down the fillRect() performance. Also note that I am not starting with a clean slate or testing the exact same set of pixels for each test.

注意,我的测试不尝试保存和恢复画布上下文fillStyle;这会降低fillRect()的性能。还要注意的是,我并不是从头开始,也不是为每个测试测试测试相同的像素集。

#2


13  

I hadn't considered fillRect(), but the answers spurned me to benchmark it against putImage().

我没有考虑fillRect(),但是答案拒绝了我将它作为putImage()的基准。

Putting 100,000 randomly coloured pixels in random locations, with Chrome 9.0.597.84 on an (old) MacBook Pro, takes less than 100ms with putImage(), but nearly 900ms using fillRect(). (Benchmark code at http://pastebin.com/4ijVKJcC).

在一个(旧的)MacBook Pro上使用Chrome 9.0.597.84,在随机位置上放置100,000个随机彩色像素,使用putImage()时花费不到100ms,但是使用fillRect()时花费了将近900ms。(基准代码在http://pastebin.com/4ijVKJcC)。

If instead I choose a single colour outside of the loops and just plot that colour at random locations, putImage() takes 59ms vs 102ms for fillRect().

如果我在循环之外选择一种颜色,并在任意位置绘制该颜色,putImage()为fillRect()花费59ms而不是102ms。

It seems that the overhead of generating and parsing a CSS colour specification in rgb(...) syntax is responsible for most of the difference.

在rgb(…)语法中生成和解析CSS颜色规范的开销似乎是大部分差异的原因。

Putting raw RGB values straight into an ImageData block on the other hand requires no string handling or parsing.

另一方面,将原始RGB值直接放入ImageData块不需要进行字符串处理或解析。

#3


9  

function setPixel(imageData, x, y, r, g, b, a) {
    index = (x + y * imageData.width) * 4;
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

#4


5  

Since different browsers seems to prefer different methods, maybe it would make sense to do a smaller test with all three methods as a part of the loading process to find out which is best to use and then use that throughout the application?

由于不同的浏览器似乎更喜欢不同的方法,所以在加载过程中使用这三种方法进行一个更小的测试,以确定哪种方法是最好的,然后在整个应用程序中使用它,这或许是有意义的。

#5


4  

What about a rectangle? That's got to be more efficient than creating an ImageData object.

一个矩形呢?这比创建ImageData对象更有效。

#6


4  

It seems strange, but nonetheless HTML5 supports drawing lines, circles, rectangles and many other basic shapes, it does not have anything suitable for drawing the basic point. The only way to do so is to simulate point with whatever you have.

这看起来很奇怪,但是尽管HTML5支持绘制线、圆、矩形和许多其他基本形状,但是它没有任何适合绘制基本点的东西。这样做的唯一方法就是用你所拥有的任何东西来模拟点。

So basically there are 3 possible solutions:

基本上有三种可能的解决方案

  • draw point as a line
  • 把点画成一条线
  • draw point as a polygon
  • 把点画成一个多边形
  • draw point as a circle
  • 把点画成一个圆

Each of them has their drawbacks

他们每个人都有自己的缺点。


Line

function point(x, y, canvas){
  canvas.beginPath();
  canvas.moveTo(x, y);
  canvas.lineTo(x+1, y+1);
  canvas.stroke();
}

Keep in mind that we are drawing to South-East direction, and if this is the edge, there can be a problem. But you can also draw in any other direction.

请记住,我们正在绘制东南方向,如果这是边缘,就会出现问题。但是你也可以画其他方向。


Rectangle

矩形

function point(x, y, canvas){
  canvas.strokeRect(x,y,1,1);
}

or in a faster way using fillRect because render engine will just fill one pixel.

或者使用fillRect更快,因为渲染引擎只填充一个像素。

function point(x, y, canvas){
  canvas.fillRect(x,y,1,1);
}

Circle


One of the problems with circles is that it is harder for an engine to render them

圆的问题之一是引擎很难渲染圆

function point(x, y, canvas){
  canvas.beginPath();
  canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
  canvas.stroke();
}

the same idea as with rectangle you can achieve with fill.

与填充矩形的想法相同。

function point(x, y, canvas){
  canvas.beginPath();
  canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
  canvas.fill();
}

Problems with all these solutions:

所有这些解决方案的问题:

  • it is hard to keep track of all the points you are going to draw.
  • 很难记住你要画的所有点。
  • when you zoom in, it looks ugly.
  • 当你放大时,它看起来很丑。

If you are wondering, "What is the best way to draw a point?", I would go with filled rectangle. You can see my jsperf here with comparison tests.

如果你想知道,“什么是画点的最好方法?”我选择填充矩形。你可以通过对比测试看到我的jsperf。

#7


1  

Hmm, you could also just make a 1 pixel wide line with a length of 1 pixel and make it's direction move along a single axis.

嗯,你也可以做一条1像素宽,长度为1像素的线,让它的方向沿着一个轴移动。

            ctx.beginPath();
            ctx.lineWidth = 1; // one pixel wide
            ctx.strokeStyle = rgba(...);
            ctx.moveTo(50,25); // positioned at 50,25
            ctx.lineTo(51,25); // one pixel long
            ctx.stroke();

#8


1  

To complete Phrogz very thorough answer, there is a critical difference between fillRect() and putImageData().
The first uses context to draw over by adding a rectangle (NOT a pixel), using the fillStyle alpha value AND the context globalAlpha and the transformation matrix, line caps etc..
The second replaces an entire set of pixels (maybe one, but why ?)
The result is different as you can see on jsperf.

要完成Phrogz非常彻底的答案,在fillRect()和putImageData()之间有一个关键的区别。第一种方法是通过添加一个矩形(不是像素)来绘制文本,使用fillStyle alpha值和上下文globalAlpha以及转换矩阵、行大写等。第二个替换了整个像素集(可能是一个,但是为什么呢?)正如你在jsperf上看到的,结果是不同的。


Nobody wants to set one pixel at a time (meaning drawing it on screen). That is why there is no specific API to do that (and rightly so).
Performance wise, if the goal is to generate a picture (for example a ray-tracing software), you always want to use an array obtained by getImageData() which is an optimized Uint8Array. Then you call putImageData() ONCE or a few times per second using setTimeout/seTInterval.

没有人希望每次设置一个像素(即在屏幕上绘制)。这就是为什么没有特定的API来实现这一点(而且是正确的)。在性能方面,如果目标是生成图片(例如,射线跟踪软件),您总是希望使用getImageData()获得的数组,这是一个优化的Uint8Array。然后使用setTimeout/seTInterval每秒调用putImageData()一次或几次。

#9


0  

Draw a rectangle like sdleihssirhc said!

画一个像sdleihssirhc说的矩形!

ctx.fillRect (10, 10, 1, 1);

^-- should draw a 1x1 rectangle at x:10, y:10

^——应该画一个矩形1 x1在x:10,y:10

#10


0  

putImageData is probably faster than fillRect natively. I think this because the fifth parameter can have different ways to be assigned (the rectangle color), using a string that must be interpreted.

putImageData可能比fillRect本身更快。我认为这是因为第五个参数可以有不同的分配方式(矩形颜色),使用必须被解释的字符串。

Suppose you're doing that:

你所做的假设:

context.fillRect(x, y, 1, 1, "#fff")
context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`
context.fillRect(x, y, 1, 1, "rgb(255,255,255)")`
context.fillRect(x, y, 1, 1, "blue")`

So, the line

所以,行

context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`

is the most heavy between all. The fifth argument in the fillRect call is a bit longer string.

是最沉重的。fillRect调用中的第五个参数是一个长一点的字符串。

#11


0  

If you are concerned about the speed then you could also consider WebGL.

如果您关心速度,那么您也可以考虑WebGL。

#12


-1  

Fast HTML Demo code: Based on what I know about SFML C++ graphics library:

快速HTML演示代码:基于我对SFML c++图形库的了解:

Save this as an HTML file with UTF-8 Encoding and run it. Feel free to refactor, I just like using japanese variables because they are concise and don't take up much space

将其保存为带有UTF-8编码的HTML文件并运行它。随意重构,我喜欢使用日语变量,因为它们很简洁,不会占用太多空间

Rarely are you going to want to set ONE arbitrary pixel and display it on the screen. So use the

你很少会想要设置一个任意的像素并在屏幕上显示它。所以使用

PutPix(x,y, r,g,b,a) 

method to draw numerous arbitrary pixels to a back-buffer. (cheap calls)

方法将多个任意像素绘制到后缓冲区。(便宜的调用)

Then when ready to show, call the

然后,当准备显示,打电话

Apply() 

method to display the changes. (expensive call)

方法显示更改。(昂贵的)

Full .HTML file code below:

完整的。html文件代码如下:

<!DOCTYPE HTML >
<html lang="en">
<head>
    <title> back-buffer demo </title>
</head>
<body>

</body>

<script>
//Main function to execute once 
//all script is loaded:
function main(){

    //Create a canvas:
    var canvas;
    canvas = attachCanvasToDom();

    //Do the pixel setting test:
    var test_type = FAST_TEST;
    backBufferTest(canvas, test_type);
}

//Constants:
var SLOW_TEST = 1;
var FAST_TEST = 2;


function attachCanvasToDom(){
    //Canvas Creation:
    //cccccccccccccccccccccccccccccccccccccccccc//
    //Create Canvas and append to body:
    var can = document.createElement('canvas');
    document.body.appendChild(can);

    //Make canvas non-zero in size, 
    //so we can see it:
    can.width = 800;
    can.height= 600;

    //Get the context, fill canvas to get visual:
    var ctx = can.getContext("2d");
    ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
    ctx.fillRect(0,0,can.width-1, can.height-1);
    //cccccccccccccccccccccccccccccccccccccccccc//

    //Return the canvas that was created:
    return can;
}

//THIS OBJECT IS SLOOOOOWW!
// 筆 == "pen"
//T筆 == "Type:Pen"
function T筆(canvas){


    //Publicly Exposed Functions
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//
    this.PutPix = _putPix;
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//

    if(!canvas){
        throw("[NilCanvasGivenToPenConstruct]");
    }

    var _ctx = canvas.getContext("2d");

    //Pixel Setting Test:
    // only do this once per page
    //絵  =="image"
    //資  =="data"
    //絵資=="image data"
    //筆  =="pen"
    var _絵資 = _ctx.createImageData(1,1); 
    // only do this once per page
    var _筆  = _絵資.data;   


    function _putPix(x,y,  r,g,b,a){
        _筆[0]   = r;
        _筆[1]   = g;
        _筆[2]   = b;
        _筆[3]   = a;
        _ctx.putImageData( _絵資, x, y );  
    }
}

//Back-buffer object, for fast pixel setting:
//尻 =="butt,rear" using to mean "back-buffer"
//T尻=="type: back-buffer"
function T尻(canvas){

    //Publicly Exposed Functions
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//
    this.PutPix = _putPix;
    this.Apply  = _apply;
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//

    if(!canvas){
        throw("[NilCanvasGivenToPenConstruct]");
    }

    var _can = canvas;
    var _ctx = canvas.getContext("2d");

    //Pixel Setting Test:
    // only do this once per page
    //絵  =="image"
    //資  =="data"
    //絵資=="image data"
    //筆  =="pen"
    var _w = _can.width;
    var _h = _can.height;
    var _絵資 = _ctx.createImageData(_w,_h); 
    // only do this once per page
    var _筆  = _絵資.data;   


    function _putPix(x,y,  r,g,b,a){

        //Convert XY to index:
        var dex = ( (y*4) *_w) + (x*4);

        _筆[dex+0]   = r;
        _筆[dex+1]   = g;
        _筆[dex+2]   = b;
        _筆[dex+3]   = a;

    }

    function _apply(){
        _ctx.putImageData( _絵資, 0,0 );  
    }

}

function backBufferTest(canvas_input, test_type){
    var can = canvas_input; //shorthand var.

    if(test_type==SLOW_TEST){
        var t筆 = new T筆( can );

        //Iterate over entire canvas, 
        //and set pixels:
        var x0 = 0;
        var x1 = can.width - 1;

        var y0 = 0;
        var y1 = can.height -1;

        for(var x = x0; x <= x1; x++){
        for(var y = y0; y <= y1; y++){
            t筆.PutPix(
                x,y, 
                x%256, y%256,(x+y)%256, 255
            );
        }}//next X/Y

    }else
    if(test_type==FAST_TEST){
        var t尻 = new T尻( can );

        //Iterate over entire canvas, 
        //and set pixels:
        var x0 = 0;
        var x1 = can.width - 1;

        var y0 = 0;
        var y1 = can.height -1;

        for(var x = x0; x <= x1; x++){
        for(var y = y0; y <= y1; y++){
            t尻.PutPix(
                x,y, 
                x%256, y%256,(x+y)%256, 255
            );
        }}//next X/Y

        //When done setting arbitrary pixels,
        //use the apply method to show them 
        //on screen:
        t尻.Apply();

    }
}


main();
</script>
</html>

#1


234  

There are two best contenders:

最好的竞争者有两个:

  1. Create a 1×1 image data, set the color, and putImageData at the location:

    创建一个1×1图像数据,设置颜色,和putImageData位置:

    var id = myContext.createImageData(1,1); // only do this once per page
    var d  = id.data;                        // only do this once per page
    d[0]   = r;
    d[1]   = g;
    d[2]   = b;
    d[3]   = a;
    myContext.putImageData( id, x, y );     
    
  2. Use fillRect() to draw a pixel (there should be no aliasing issues):

    使用fillRect()绘制像素(不存在混叠问题):

    ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
    ctx.fillRect( x, y, 1, 1 );
    

You can test the speed of these here: http://jsperf.com/setting-canvas-pixel/9 or here https://www.measurethat.net/Benchmarks/Show/1664/1

您可以在这里测试它们的速度:http://jsperf.com/setting-canvas-pixel/9,或者https://www.measurethat.net/Benchmarks/Show/1664/1

I recommend testing against browsers you care about for maximum speed. As of July 2017, fillRect() is 5-6× faster on Firefox v54 and Chrome v59 (Win7x64).

我建议针对您所关心的浏览器进行测试,以获得最高的速度。截至2017年7月,fillRect()5 - 6×速度v54 Firefox和Chrome v59(Win7x64)。

Other, sillier alternatives are:

其他愚蠢的选择是:

  • using getImageData()/putImageData() on the entire canvas; this is about 100× slower than other options.

    在整个画布上使用getImageData()/putImageData();这是关于100×低于其他选项。

  • creating a custom image using a data url and using drawImage() to show it:

    使用数据url创建自定义映像并使用drawImage()来显示:

    var img = new Image;
    img.src = "data:image/png;base64," + myPNGEncoder(r,g,b,a);
    // Writing the PNGEncoder is left as an exercise for the reader
    
  • creating another img or canvas filled with all the pixels you want and use drawImage() to blit just the pixel you want across. This would probably be very fast, but has the limitation that you need to pre-calculate the pixels you need.

    创建另一个img或canvas,填充所有你想要的像素,并使用drawImage()来设置你想要的像素。这可能非常快,但是有一个限制,就是您需要预先计算您需要的像素。

Note that my tests do not attempt to save and restore the canvas context fillStyle; this would slow down the fillRect() performance. Also note that I am not starting with a clean slate or testing the exact same set of pixels for each test.

注意,我的测试不尝试保存和恢复画布上下文fillStyle;这会降低fillRect()的性能。还要注意的是,我并不是从头开始,也不是为每个测试测试测试相同的像素集。

#2


13  

I hadn't considered fillRect(), but the answers spurned me to benchmark it against putImage().

我没有考虑fillRect(),但是答案拒绝了我将它作为putImage()的基准。

Putting 100,000 randomly coloured pixels in random locations, with Chrome 9.0.597.84 on an (old) MacBook Pro, takes less than 100ms with putImage(), but nearly 900ms using fillRect(). (Benchmark code at http://pastebin.com/4ijVKJcC).

在一个(旧的)MacBook Pro上使用Chrome 9.0.597.84,在随机位置上放置100,000个随机彩色像素,使用putImage()时花费不到100ms,但是使用fillRect()时花费了将近900ms。(基准代码在http://pastebin.com/4ijVKJcC)。

If instead I choose a single colour outside of the loops and just plot that colour at random locations, putImage() takes 59ms vs 102ms for fillRect().

如果我在循环之外选择一种颜色,并在任意位置绘制该颜色,putImage()为fillRect()花费59ms而不是102ms。

It seems that the overhead of generating and parsing a CSS colour specification in rgb(...) syntax is responsible for most of the difference.

在rgb(…)语法中生成和解析CSS颜色规范的开销似乎是大部分差异的原因。

Putting raw RGB values straight into an ImageData block on the other hand requires no string handling or parsing.

另一方面,将原始RGB值直接放入ImageData块不需要进行字符串处理或解析。

#3


9  

function setPixel(imageData, x, y, r, g, b, a) {
    index = (x + y * imageData.width) * 4;
    imageData.data[index+0] = r;
    imageData.data[index+1] = g;
    imageData.data[index+2] = b;
    imageData.data[index+3] = a;
}

#4


5  

Since different browsers seems to prefer different methods, maybe it would make sense to do a smaller test with all three methods as a part of the loading process to find out which is best to use and then use that throughout the application?

由于不同的浏览器似乎更喜欢不同的方法,所以在加载过程中使用这三种方法进行一个更小的测试,以确定哪种方法是最好的,然后在整个应用程序中使用它,这或许是有意义的。

#5


4  

What about a rectangle? That's got to be more efficient than creating an ImageData object.

一个矩形呢?这比创建ImageData对象更有效。

#6


4  

It seems strange, but nonetheless HTML5 supports drawing lines, circles, rectangles and many other basic shapes, it does not have anything suitable for drawing the basic point. The only way to do so is to simulate point with whatever you have.

这看起来很奇怪,但是尽管HTML5支持绘制线、圆、矩形和许多其他基本形状,但是它没有任何适合绘制基本点的东西。这样做的唯一方法就是用你所拥有的任何东西来模拟点。

So basically there are 3 possible solutions:

基本上有三种可能的解决方案

  • draw point as a line
  • 把点画成一条线
  • draw point as a polygon
  • 把点画成一个多边形
  • draw point as a circle
  • 把点画成一个圆

Each of them has their drawbacks

他们每个人都有自己的缺点。


Line

function point(x, y, canvas){
  canvas.beginPath();
  canvas.moveTo(x, y);
  canvas.lineTo(x+1, y+1);
  canvas.stroke();
}

Keep in mind that we are drawing to South-East direction, and if this is the edge, there can be a problem. But you can also draw in any other direction.

请记住,我们正在绘制东南方向,如果这是边缘,就会出现问题。但是你也可以画其他方向。


Rectangle

矩形

function point(x, y, canvas){
  canvas.strokeRect(x,y,1,1);
}

or in a faster way using fillRect because render engine will just fill one pixel.

或者使用fillRect更快,因为渲染引擎只填充一个像素。

function point(x, y, canvas){
  canvas.fillRect(x,y,1,1);
}

Circle


One of the problems with circles is that it is harder for an engine to render them

圆的问题之一是引擎很难渲染圆

function point(x, y, canvas){
  canvas.beginPath();
  canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
  canvas.stroke();
}

the same idea as with rectangle you can achieve with fill.

与填充矩形的想法相同。

function point(x, y, canvas){
  canvas.beginPath();
  canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
  canvas.fill();
}

Problems with all these solutions:

所有这些解决方案的问题:

  • it is hard to keep track of all the points you are going to draw.
  • 很难记住你要画的所有点。
  • when you zoom in, it looks ugly.
  • 当你放大时,它看起来很丑。

If you are wondering, "What is the best way to draw a point?", I would go with filled rectangle. You can see my jsperf here with comparison tests.

如果你想知道,“什么是画点的最好方法?”我选择填充矩形。你可以通过对比测试看到我的jsperf。

#7


1  

Hmm, you could also just make a 1 pixel wide line with a length of 1 pixel and make it's direction move along a single axis.

嗯,你也可以做一条1像素宽,长度为1像素的线,让它的方向沿着一个轴移动。

            ctx.beginPath();
            ctx.lineWidth = 1; // one pixel wide
            ctx.strokeStyle = rgba(...);
            ctx.moveTo(50,25); // positioned at 50,25
            ctx.lineTo(51,25); // one pixel long
            ctx.stroke();

#8


1  

To complete Phrogz very thorough answer, there is a critical difference between fillRect() and putImageData().
The first uses context to draw over by adding a rectangle (NOT a pixel), using the fillStyle alpha value AND the context globalAlpha and the transformation matrix, line caps etc..
The second replaces an entire set of pixels (maybe one, but why ?)
The result is different as you can see on jsperf.

要完成Phrogz非常彻底的答案,在fillRect()和putImageData()之间有一个关键的区别。第一种方法是通过添加一个矩形(不是像素)来绘制文本,使用fillStyle alpha值和上下文globalAlpha以及转换矩阵、行大写等。第二个替换了整个像素集(可能是一个,但是为什么呢?)正如你在jsperf上看到的,结果是不同的。


Nobody wants to set one pixel at a time (meaning drawing it on screen). That is why there is no specific API to do that (and rightly so).
Performance wise, if the goal is to generate a picture (for example a ray-tracing software), you always want to use an array obtained by getImageData() which is an optimized Uint8Array. Then you call putImageData() ONCE or a few times per second using setTimeout/seTInterval.

没有人希望每次设置一个像素(即在屏幕上绘制)。这就是为什么没有特定的API来实现这一点(而且是正确的)。在性能方面,如果目标是生成图片(例如,射线跟踪软件),您总是希望使用getImageData()获得的数组,这是一个优化的Uint8Array。然后使用setTimeout/seTInterval每秒调用putImageData()一次或几次。

#9


0  

Draw a rectangle like sdleihssirhc said!

画一个像sdleihssirhc说的矩形!

ctx.fillRect (10, 10, 1, 1);

^-- should draw a 1x1 rectangle at x:10, y:10

^——应该画一个矩形1 x1在x:10,y:10

#10


0  

putImageData is probably faster than fillRect natively. I think this because the fifth parameter can have different ways to be assigned (the rectangle color), using a string that must be interpreted.

putImageData可能比fillRect本身更快。我认为这是因为第五个参数可以有不同的分配方式(矩形颜色),使用必须被解释的字符串。

Suppose you're doing that:

你所做的假设:

context.fillRect(x, y, 1, 1, "#fff")
context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`
context.fillRect(x, y, 1, 1, "rgb(255,255,255)")`
context.fillRect(x, y, 1, 1, "blue")`

So, the line

所以,行

context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`

is the most heavy between all. The fifth argument in the fillRect call is a bit longer string.

是最沉重的。fillRect调用中的第五个参数是一个长一点的字符串。

#11


0  

If you are concerned about the speed then you could also consider WebGL.

如果您关心速度,那么您也可以考虑WebGL。

#12


-1  

Fast HTML Demo code: Based on what I know about SFML C++ graphics library:

快速HTML演示代码:基于我对SFML c++图形库的了解:

Save this as an HTML file with UTF-8 Encoding and run it. Feel free to refactor, I just like using japanese variables because they are concise and don't take up much space

将其保存为带有UTF-8编码的HTML文件并运行它。随意重构,我喜欢使用日语变量,因为它们很简洁,不会占用太多空间

Rarely are you going to want to set ONE arbitrary pixel and display it on the screen. So use the

你很少会想要设置一个任意的像素并在屏幕上显示它。所以使用

PutPix(x,y, r,g,b,a) 

method to draw numerous arbitrary pixels to a back-buffer. (cheap calls)

方法将多个任意像素绘制到后缓冲区。(便宜的调用)

Then when ready to show, call the

然后,当准备显示,打电话

Apply() 

method to display the changes. (expensive call)

方法显示更改。(昂贵的)

Full .HTML file code below:

完整的。html文件代码如下:

<!DOCTYPE HTML >
<html lang="en">
<head>
    <title> back-buffer demo </title>
</head>
<body>

</body>

<script>
//Main function to execute once 
//all script is loaded:
function main(){

    //Create a canvas:
    var canvas;
    canvas = attachCanvasToDom();

    //Do the pixel setting test:
    var test_type = FAST_TEST;
    backBufferTest(canvas, test_type);
}

//Constants:
var SLOW_TEST = 1;
var FAST_TEST = 2;


function attachCanvasToDom(){
    //Canvas Creation:
    //cccccccccccccccccccccccccccccccccccccccccc//
    //Create Canvas and append to body:
    var can = document.createElement('canvas');
    document.body.appendChild(can);

    //Make canvas non-zero in size, 
    //so we can see it:
    can.width = 800;
    can.height= 600;

    //Get the context, fill canvas to get visual:
    var ctx = can.getContext("2d");
    ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
    ctx.fillRect(0,0,can.width-1, can.height-1);
    //cccccccccccccccccccccccccccccccccccccccccc//

    //Return the canvas that was created:
    return can;
}

//THIS OBJECT IS SLOOOOOWW!
// 筆 == "pen"
//T筆 == "Type:Pen"
function T筆(canvas){


    //Publicly Exposed Functions
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//
    this.PutPix = _putPix;
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//

    if(!canvas){
        throw("[NilCanvasGivenToPenConstruct]");
    }

    var _ctx = canvas.getContext("2d");

    //Pixel Setting Test:
    // only do this once per page
    //絵  =="image"
    //資  =="data"
    //絵資=="image data"
    //筆  =="pen"
    var _絵資 = _ctx.createImageData(1,1); 
    // only do this once per page
    var _筆  = _絵資.data;   


    function _putPix(x,y,  r,g,b,a){
        _筆[0]   = r;
        _筆[1]   = g;
        _筆[2]   = b;
        _筆[3]   = a;
        _ctx.putImageData( _絵資, x, y );  
    }
}

//Back-buffer object, for fast pixel setting:
//尻 =="butt,rear" using to mean "back-buffer"
//T尻=="type: back-buffer"
function T尻(canvas){

    //Publicly Exposed Functions
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//
    this.PutPix = _putPix;
    this.Apply  = _apply;
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//

    if(!canvas){
        throw("[NilCanvasGivenToPenConstruct]");
    }

    var _can = canvas;
    var _ctx = canvas.getContext("2d");

    //Pixel Setting Test:
    // only do this once per page
    //絵  =="image"
    //資  =="data"
    //絵資=="image data"
    //筆  =="pen"
    var _w = _can.width;
    var _h = _can.height;
    var _絵資 = _ctx.createImageData(_w,_h); 
    // only do this once per page
    var _筆  = _絵資.data;   


    function _putPix(x,y,  r,g,b,a){

        //Convert XY to index:
        var dex = ( (y*4) *_w) + (x*4);

        _筆[dex+0]   = r;
        _筆[dex+1]   = g;
        _筆[dex+2]   = b;
        _筆[dex+3]   = a;

    }

    function _apply(){
        _ctx.putImageData( _絵資, 0,0 );  
    }

}

function backBufferTest(canvas_input, test_type){
    var can = canvas_input; //shorthand var.

    if(test_type==SLOW_TEST){
        var t筆 = new T筆( can );

        //Iterate over entire canvas, 
        //and set pixels:
        var x0 = 0;
        var x1 = can.width - 1;

        var y0 = 0;
        var y1 = can.height -1;

        for(var x = x0; x <= x1; x++){
        for(var y = y0; y <= y1; y++){
            t筆.PutPix(
                x,y, 
                x%256, y%256,(x+y)%256, 255
            );
        }}//next X/Y

    }else
    if(test_type==FAST_TEST){
        var t尻 = new T尻( can );

        //Iterate over entire canvas, 
        //and set pixels:
        var x0 = 0;
        var x1 = can.width - 1;

        var y0 = 0;
        var y1 = can.height -1;

        for(var x = x0; x <= x1; x++){
        for(var y = y0; y <= y1; y++){
            t尻.PutPix(
                x,y, 
                x%256, y%256,(x+y)%256, 255
            );
        }}//next X/Y

        //When done setting arbitrary pixels,
        //use the apply method to show them 
        //on screen:
        t尻.Apply();

    }
}


main();
</script>
</html>