回答:
2つの最高の候補があります。
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 );
fillRect()
ピクセルの描画に使用します(エイリアシングの問題はありません)。
ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
ctx.fillRect( x, y, 1, 1 );
ここでこれらの速度をテストできます:http : //jsperf.com/setting-canvas-pixel/9またはここhttps://www.measurethat.net/Benchmarks/Show/1664/1
最大速度を重視するブラウザに対してテストすることをお勧めします。2017年7月の時点で、fillRect()
Firefox v54とChrome v59(Win7x64)では5〜6倍高速です。
その他、愚かな代替案は次のとおりです。
getImageData()/putImageData()
キャンバス全体で使用します。これは他のオプションよりも約100倍遅いです。
データ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
必要なすべてのピクセルで満たされた別のimgまたはキャンバスを作成し、必要なピクセルdrawImage()
だけをブリットするために使用します。これはおそらく非常に高速ですが、必要なピクセルを事前に計算する必要があるという制限があります。
テストではキャンバスコンテキストの保存と復元を試みないことに注意してくださいfillStyle
。これにより、fillRect()
パフォーマンスが低下します。また、私は白紙の状態から始めたり、各テストでまったく同じピクセルセットをテストしたりしていないことにも注意してください。
fillRect()
Chromev24の1x1 putimagedataと比べて、最近ではほぼ10倍高速になったことに注意してください。だから...スピードが重要で、あなたがあなたのターゲットオーディエンスを知っているなら、時代遅れの答えの言葉を取らないでください(私のものでも)。代わりに:テストしてください!
言及されていない1つの方法は、getImageDataを使用してからputImageDataを使用することです。
この方法は、一度にたくさん描画したい場合に適しています。
http://next.plnkr.co/edit/mfNyalsAR2MWkccr
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
var id = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
var pixels = id.data;
var x = Math.floor(Math.random() * canvasWidth);
var y = Math.floor(Math.random() * canvasHeight);
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
var off = (y * id.width + x) * 4;
pixels[off] = r;
pixels[off + 1] = g;
pixels[off + 2] = b;
pixels[off + 3] = 255;
ctx.putImageData(id, 0, 0);
私は考慮していませんでしたfillRect()
が、答えはそれをベンチマークに駆り立てましたputImage()
。
(古い)MacBook ProでChrome 9.0.597.84を使用して100,000個のランダムに色付けされたピクセルをランダムな場所に配置すると、を使用すると100ミリ秒未満かかりますが、をputImage()
使用すると900ミリ秒近くかかりfillRect()
ます。(http://pastebin.com/4ijVKJcCのベンチマークコード)。
代わりに、ループの外で単一の色を選択し、その色をランダムな場所にプロットするだけの場合、putImage()
59ms vs 102msかかりますfillRect()
。
rgb(...)
違いのほとんどは、構文でのCSSカラー仕様の生成と解析のオーバーヘッドが原因であると思われます。
ImageData
一方、生のRGB値をブロックに直接入力する場合、文字列の処理や解析は必要ありません。
function setPixel(imageData, x, y, r, g, b, a) {
var index = 4 * (x + y * imageData.width);
imageData.data[index+0] = r;
imageData.data[index+1] = g;
imageData.data[index+2] = b;
imageData.data[index+3] = a;
}
putImageData()
その関数の後に呼び出す必要がありますか、参照によってコンテキストが更新されますか?
奇妙に思われるかもしれませんが、HTML5は線、円、長方形、およびその他の多くの基本的な形状の描画をサポートしていますが、基本的な点を描画するのに適したものはありません。そうする唯一の方法は、あなたが持っているものでポイントをシミュレートすることです。
したがって、基本的には3つの解決策があります。
それぞれに欠点があります
ライン
function point(x, y, canvas){
canvas.beginPath();
canvas.moveTo(x, y);
canvas.lineTo(x+1, y+1);
canvas.stroke();
}
私たちは南東の方向に向かっているので、これが端である場合は問題が発生する可能性があることに注意してください。ただし、他の方向にも描画できます。
矩形
function point(x, y, canvas){
canvas.strokeRect(x,y,1,1);
}
または、レンダリングエンジンが1ピクセルを塗りつぶすだけなので、fillRectを使用するとより高速になります。
function point(x, y, canvas){
canvas.fillRect(x,y,1,1);
}
サークル
サークルの問題の1つは、エンジンがそれらをレンダリングするのが難しいことです
function point(x, y, canvas){
canvas.beginPath();
canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
canvas.stroke();
}
塗りつぶしで達成できる長方形と同じアイデア。
function point(x, y, canvas){
canvas.beginPath();
canvas.arc(x, y, 1, 0, 2 * Math.PI, true);
canvas.fill();
}
これらすべてのソリューションの問題:
「点を描くのに最適な方法は何ですか?」と疑問に思っている場合は、塗りつぶされた長方形を使用します。私のjsperfは、比較テストで確認できます。
長方形はどうですか?ImageData
オブジェクトを作成するよりも効率的です。
putImageData
するとfillRect
、Chromeの場合よりも10倍速くなります。(詳細については、私の回答を参照してください。)
うーん、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();
Phrogzの非常に完全な答えを完了するには、との間に重要な違いがfillRect()
ありputImageData()
ます。
1つ目は、コンテキストを使用して、fillStyleアルファ値とコンテキストglobalAlpha、変換行列、ラインキャップなどを使用して、四角形(ピクセルではない)を追加することで描画します。2つ目は、ピクセルセット全体(おそらく1つ)を置き換えます?)
jsperfで確認できるように、結果は異なります。
だれも一度に1つのピクセルを設定することは望んでいません(画面に描画することを意味します)。そのため、それを行うための特定のAPIはありません(その通りです)。
パフォーマンスに関しては、目的が画像(たとえば、レイトレーシングソフトウェア)を生成することである場合、常にgetImageData()
、最適化されたUint8Arrayであるによって取得された配列を使用する必要があります。次に、putImageData()
ONCE を呼び出すか、を使用して1秒あたり数回呼び出しますsetTimeout/seTInterval
。
fillRect
Chromeのハードウェアアクセラレーションは、必要なGPUへの個々の呼び出しに対応できないため、使用は苦痛でした。ピクセルデータを1:1で使用し、CSSスケーリングを使用して目的の出力を取得する必要がありました。醜い:(
get/putImageData
オペレーション/秒しか得られませんが、には194,893が得られfillRect
ます。1x1 image data
125,102 Ops /秒です。それでfillRect
、Firefoxで圧倒的に勝ちました。したがって、2012年と今日の間で状況は大きく変化しました。いつものように、決して古いベンチマーク結果に依存しないでください。
高速HTMLデモコード: 私がSFML C ++グラフィックライブラリについて知っていることに基づいて:
これをHTMLファイルとしてUTF-8エンコーディングで保存し、実行します。 自由にリファクタリングしてください。日本語の変数を使用するのは好きです。なぜなら、それらは簡潔であり、あまりスペースを取らないからです。
まれに、任意のピクセルを1つ設定して画面に表示したい場合があります。したがって、
PutPix(x,y, r,g,b,a)
多数の任意のピクセルをバックバッファに描画する方法。(格安通話)
次に、表示する準備ができたら、
Apply()
変更を表示するメソッド。(高価な電話)
以下の完全な.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>
ハンディとプットピクセル(pp)関数(ES6)の命題(ここでピクセルを読み取る):
let pp= ((s='.myCanvas',c=document.querySelector(s),ctx=c.getContext('2d'),id=ctx.createImageData(1,1)) => (x,y,r=0,g=0,b=0,a=255)=>(id.data.set([r,g,b,a]),ctx.putImageData(id, x, y),c))()
pp(10,30,0,0,255,255); // x,y,r,g,b,a ; return canvas object
この関数は使用しputImageData
、初期化部分(最初の長い行)を持っています。最初に、代わりにs='.myCanvas'
キャンバスにCSSセレクターを使用します。
あなたがしたい私はあなたがデフォルト値を変更する必要が0-1からの値にパラメータを正常化するためa=255
にa=1
持つとライン:
id.data.set([r,g,b,a]),ctx.putImageData(id, x, y)
に
id.data.set([r*255,g*255,b*255,a*255]),ctx.putImageData(id, x*c.width, y*c.height)
上記の便利なコードは、グラフィックスアルゴリズムのアドホックテストや概念実証に適していますが、コードを読みやすく明確にする必要がある本番環境での使用には適していません。
putImageData
おそらくfillRect
ネイティブよりも高速です。これは、解釈する必要がある文字列を使用して、5番目のパラメーターに異なる方法(四角形の色)を割り当てることができるためです。
あなたがそうしているとしましょう:
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")`
だから、ライン
context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`
すべての中で最も重いです。fillRect
呼び出しの5番目の引数は少し長い文字列です。
context.fillStyle = ...
代わりに使用する必要がありました。developer.mozilla.org/en-US/docs/Web/API/...